Dynamic Camera System
The bittyBIG Dynamic Camera System gives Stencyl projects a more flexible camera and output pipeline. It supports zoom, tweening, split-screen, picture-in-picture, viewport transforms, camera rotation, screenshake, and pixel-perfect display behavior.
Project Setup in Stencyl
Before adding camera blocks, set the project up so Stencyl, your art scale, and the camera output are all speaking the same language. Otherwise the camera system works, but the project settings quietly wear fake glasses and pretend they are helping.
1. Create and enable
- Create a new Stencyl project.
- Enable the bittyBIG Dynamic Camera System extension.
- Restart/open the project if Stencyl needs to reload extension blocks.
2. Set the screen size
- Go to Game Settings → Settings → Display.
- Set Screen Size to the output size you want Stencyl to present.
- For a 320x240 pixel-art target, use 320x240 unless you intentionally want a larger canvas/window.
3. Pixel-art settings
Go to Game Settings → Settings → Advanced.
- Set Antialiasing to Off.
- Set Pixel-Snapping to Off if you want sprites to move smoothly with the camera instead of jittering on the pixel grid.
4. Project scales
For the most common pixel-art/upscale workflow:
- Keep Project Scales at 1x.
- Disable the other scales.
- Set Default Scale to 1x.
5. Platform fullscreen/window behavior
For Windows, Web, and Mobile exports, fullscreen behavior should be based around Scale to Fit / Letterbox style output. When the Stencyl screen size, camera size, and scaling target match cleanly, windowed mode and fullscreen mode behave predictably.
<window resizable="true" if="html5" />
Quick Start Guide
Once the project settings are clean, start with this setup and confirm the camera is drawing before adding presets, zoom, split-screen, or anything fancy.
Scene Setup
The Dynamic Camera System renders your Stencyl scene into a final composite image, then displays that image on a reserved layer. A little scene setup keeps Stencyl layers, parallax backgrounds, and UI overlays behaving predictably.
1. Make a layer named UI
Create a Stencyl layer named UI in every scene that needs screen overlays. This name is reserved by the camera system.
- Set the UI layer scroll factors to 0, 0.
- Put HUD actors, text, icons, menus, and screen-space overlays on this layer.
- Position UI objects as screen/HUD objects, not world objects.
2. Keep UI above every other layer
The UI layer is not copied into the camera render. It stays as a normal Stencyl layer and should be kept above every other layer in the scene so it draws over the final game image.
- Use UI for things that should not move with the camera.
- Use the UI layer only for final screen overlays.
- Do not use UI for gameplay actors that belong inside the camera view.
- If you add layers at runtime, make sure UI stays on top afterward.
3. Background and parallax layers
Stencyl background layers can stay in the normal scene layer stack. Custom scroll factors are supported, so parallax backgrounds should move according to their layer settings.
- Place background layers wherever they belong in the scene order.
- Layers above your gameplay layer can still render into the camera image.
- Parallax layers should use their normal Stencyl background/custom scroll settings.
Common Setups
Full-screen pixel-perfect camera
Use this when one camera fills the full 1920x1080 canvas. A 320x180 camera gives a clean 6x view.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<copy format="1">
<group eventID="-1" id="-1">
<bbcam-init-dynamic-camera-system comment="false" x="0" y="0"/>
<bbcam-create-camera comment="false" x="0" y="0">
<int id="0" val="0"/>
</bbcam-create-camera>
<bbcam-set-camera-size comment="false" x="0" y="0">
<int id="0" val="320"/>
<int id="1" val="180"/>
<int id="2" val="0"/>
</bbcam-set-camera-size>
</group>
</copy>
scripts.BittyCameraSystem.init(); scripts.BittyCameraBlocks.createCamera(0); scripts.BittyCameraBlocks.setCameraSize(320, 180, 0); scripts.BittyCameraBlocks.setCameraCenterToActor(player, 0);
Two-player left/right split screen
For a 1920x1080 canvas, each half is 960x1080. A 160x180 camera gives each viewport a clean 6x scale.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<copy format="1">
<group eventID="-1" id="-1">
<bbcam-init-dynamic-camera-system comment="false" x="0" y="0"/>
<bbcam-create-camera comment="false" x="0" y="0">
<int id="0" val="0"/>
</bbcam-create-camera>
<bbcam-create-camera comment="false" x="0" y="0">
<int id="0" val="1"/>
</bbcam-create-camera>
<bbcam-set-viewport-rect comment="false" x="0" y="0">
<int id="0" val="0"/>
<int id="1" val="0"/>
<int id="2" val="0"/>
<int id="3" val="960"/>
<int id="4" val="1080"/>
</bbcam-set-viewport-rect>
<bbcam-set-viewport-rect comment="false" x="0" y="0">
<int id="0" val="1"/>
<int id="1" val="960"/>
<int id="2" val="0"/>
<int id="3" val="960"/>
<int id="4" val="1080"/>
</bbcam-set-viewport-rect>
<bbcam-set-camera-size comment="false" x="0" y="0">
<int id="0" val="160"/>
<int id="1" val="180"/>
<int id="2" val="0"/>
</bbcam-set-camera-size>
<bbcam-set-camera-size comment="false" x="0" y="0">
<int id="0" val="160"/>
<int id="1" val="180"/>
<int id="2" val="1"/>
</bbcam-set-camera-size>
</group>
</copy>
scripts.BittyCameraSystem.init(); scripts.BittyCameraBlocks.createCamera(0); scripts.BittyCameraBlocks.createCamera(1); scripts.BittyCameraBlocks.setDisplayPortRect(0, 0, 0, 960, 1080); scripts.BittyCameraBlocks.setDisplayPortRect(1, 960, 0, 960, 1080); scripts.BittyCameraBlocks.setCameraSize(160, 180, 0); scripts.BittyCameraBlocks.setCameraSize(160, 180, 1);
Picture-in-picture camera
Use one main camera and one smaller overlay camera. This example uses a full 1920x1080 main viewport with a 440x248 overlay.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<copy format="1">
<group eventID="-1" id="-1">
<bbcam-init-dynamic-camera-system comment="false" x="0" y="0"/>
<bbcam-create-camera comment="false" x="0" y="0">
<int id="0" val="0"/>
</bbcam-create-camera>
<bbcam-create-camera comment="false" x="0" y="0">
<int id="0" val="1"/>
</bbcam-create-camera>
<bbcam-set-viewport-rect comment="false" x="0" y="0">
<int id="0" val="0"/>
<int id="1" val="0"/>
<int id="2" val="0"/>
<int id="3" val="1920"/>
<int id="4" val="1080"/>
</bbcam-set-viewport-rect>
<bbcam-set-viewport-rect comment="false" x="0" y="0">
<int id="0" val="1"/>
<int id="1" val="1440"/>
<int id="2" val="40"/>
<int id="3" val="440"/>
<int id="4" val="248"/>
</bbcam-set-viewport-rect>
<bbcam-set-camera-size comment="false" x="0" y="0">
<int id="0" val="320"/>
<int id="1" val="180"/>
<int id="2" val="0"/>
</bbcam-set-camera-size>
<bbcam-set-camera-size comment="false" x="0" y="0">
<int id="0" val="110"/>
<int id="1" val="62"/>
<int id="2" val="1"/>
</bbcam-set-camera-size>
</group>
</copy>
scripts.BittyCameraSystem.init(); scripts.BittyCameraBlocks.createCamera(0); scripts.BittyCameraBlocks.createCamera(1); scripts.BittyCameraBlocks.setDisplayPortRect(0, 0, 0, 1920, 1080); scripts.BittyCameraBlocks.setDisplayPortRect(1, 1440, 40, 440, 248); scripts.BittyCameraBlocks.setCameraSize(320, 180, 0); scripts.BittyCameraBlocks.setCameraSize(110, 62, 1);
Smaller Display Area
For a smaller centered-style setup, use a 1440x810 Display Area. Left/right halves are 720x810.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<copy format="1">
<group eventID="-1" id="-1">
<bbcam-init-dynamic-camera-system comment="false" x="0" y="0"/>
<bbcam-set-display-area-rect comment="false" x="0" y="0">
<int id="0" val="0"/>
<int id="1" val="0"/>
<int id="2" val="1440"/>
<int id="3" val="810"/>
</bbcam-set-display-area-rect>
<bbcam-create-camera comment="false" x="0" y="0">
<int id="0" val="0"/>
</bbcam-create-camera>
<bbcam-create-camera comment="false" x="0" y="0">
<int id="0" val="1"/>
</bbcam-create-camera>
<bbcam-set-viewport-rect comment="false" x="0" y="0">
<int id="0" val="0"/>
<int id="1" val="0"/>
<int id="2" val="0"/>
<int id="3" val="720"/>
<int id="4" val="810"/>
</bbcam-set-viewport-rect>
<bbcam-set-viewport-rect comment="false" x="0" y="0">
<int id="0" val="1"/>
<int id="1" val="720"/>
<int id="2" val="0"/>
<int id="3" val="720"/>
<int id="4" val="810"/>
</bbcam-set-viewport-rect>
<bbcam-set-camera-size comment="false" x="0" y="0">
<int id="0" val="120"/>
<int id="1" val="135"/>
<int id="2" val="0"/>
</bbcam-set-camera-size>
<bbcam-set-camera-size comment="false" x="0" y="0">
<int id="0" val="120"/>
<int id="1" val="135"/>
<int id="2" val="1"/>
</bbcam-set-camera-size>
</group>
</copy>
scripts.BittyCameraSystem.init(); scripts.BittyCameraBlocks.setGameAreaRect(0, 0, 1440, 810); scripts.BittyCameraBlocks.createCamera(0); scripts.BittyCameraBlocks.createCamera(1); scripts.BittyCameraBlocks.setDisplayPortRect(0, 0, 0, 720, 810); scripts.BittyCameraBlocks.setDisplayPortRect(1, 720, 0, 720, 810); scripts.BittyCameraBlocks.setCameraSize(120, 135, 0); scripts.BittyCameraBlocks.setCameraSize(120, 135, 1);
Interactive Viewport Setup Builder
Preview what the final layout will look like, then generate setup code for your project. Viewports split the game canvas, not the full screen.
Block Glossary
This glossary is organized more like Stencyl’s block guide: blocks first, descriptions second, code shown directly under each block.
Key Terms
Display Area - The final rectangle where the whole bittyBIG output appears on the screen.
Viewport - A rectangle inside the Display Area. It controls where a camera image appears.
Camera Size - How much world the camera sees before zoom. This is your low-resolution world view size.
Zoom - Changes how much of the world the camera sees. Higher zoom means a closer view.
Viewport Coordinates - Based on the current Display Area size.
Cameras
Setup
Initialize System
scripts.BittyCameraSystem.init();
Create Camera
scripts.BittyCameraBlocks.createCamera(~);
Render Camera to Viewport
scripts.BittyCameraBlocks.renderCameraToDisplayPort(~,~);
Set Camera Size
scripts.BittyViewports.setViewportSize(~, ~, ~);
Set Camera Scene Lock
scripts.BittyCameraBlocks.setCameraSceneLock(~,~);
Remove Camera
scripts.BittyCameraBlocks.removeCamera(~);
Presets
Apply Camera Preset
scripts.BittyCameraBlocks.applyCameraPreset(~,~);
Apply Camera Preset by Name
scripts.BittyCameraBlocks.applyCameraPreset(~,~);
Position
Set Camera Center
scripts.BittyViewports.setCameraCenter(~, ~, ~);
Set Camera Offsets
scripts.BittyCameraBlocks.setCameraOffsets(~,~,~);
Get Camera Offset X
scripts.BittyCameraBlocks.getCameraOffsetX(~)
Get Camera Offset Y
scripts.BittyCameraBlocks.getCameraOffsetY(~)
Center Camera to Actor
scripts.BittyCameraBlocks.setCameraCenterToActor(~,~);
Movement
Set Camera Target Center
scripts.BittyCameraBlocks.setCameraTargetCenter(~,~,~);
Set Camera Target to Actor
scripts.BittyCameraBlocks.setCameraTargetCenterToActor(~,~);
Set Follow Style
scripts.BittyCameraBlocks.setCameraFollowStyle(~,~);
Set Follow Speeds
scripts.BittyCameraBlocks.setCameraFollowSpeeds(~,~,~);
Set Lerp Speed
scripts.BittyCameraBlocks.setCameraLerpSpeed(~,~);
Set Zero Radius
scripts.BittyCameraBlocks.setCameraZeroRadius(~,~);
Rotation
Set Camera Rotation
scripts.BittyCameraBlocks.setCameraRotation(~,~);
Get Camera Rotation
scripts.BittyCameraBlocks.getCameraRotation(~)
Reset Camera Rotation
scripts.BittyCameraBlocks.resetCameraRotation(~);
Properties
Camera Exists?
scripts.BittyViewports.cameraExists(~)
Camera Is Transitioning?
scripts.BittyCameraBlocks.isCameraTransitioning(~)
Get Camera X
scripts.BittyViewports.getCameraX(~)
Get Camera Y
scripts.BittyViewports.getCameraY(~)
Get Camera Center X
scripts.BittyViewports.getCameraCenterX(~)
Get Camera Center Y
scripts.BittyViewports.getCameraCenterY(~)
Get Camera Width
scripts.BittyViewports.getViewportWidth(~)
Get Camera Height
scripts.BittyViewports.getViewportHeight(~)
Viewports
Setup
Create Viewport
scripts.BittyCameraBlocks.createDisplayPort(~,~,~,~,~);
Set Viewport Rect
scripts.BittyCameraBlocks.setDisplayPortRect(~,~,~,~,~);
Set Viewport Position
scripts.BittyCameraBlocks.setDisplayPortPosition(~,~,~);
Set Viewport Size
scripts.BittyCameraBlocks.setDisplayPortSize(~,~,~);
Remove Viewport
scripts.BittyCameraBlocks.removeDisplayPort(~);
Appearance
Viewport Background Color
scripts.BittyCameraBlocks.setDisplayPortBackgroundColor(~,~,~);
Clear Viewport Background
scripts.BittyCameraBlocks.clearDisplayPortBackground(~);
Enable Viewport Background
scripts.BittyCameraBlocks.setDisplayPortBackgroundEnabled(~,~);
Viewport Border
scripts.BittyCameraBlocks.setDisplayPortBorder(~,~,~,~);
Viewport Scale Mode
scripts.BittyCameraBlocks.setDisplayPortScaleMode(~,~);
Viewport Transforms
Viewport Rotation
scripts.BittyCameraBlocks.setDisplayPortRotation(~,~);
Flip Viewport Horizontally
scripts.BittyCameraBlocks.setDisplayPortFlipHorizontal(~,~);
Flip Viewport Vertically
scripts.BittyCameraBlocks.setDisplayPortFlipVertical(~,~);
Reset Viewport Transform
scripts.BittyCameraBlocks.resetDisplayPortTransform(~);
Properties
Viewport Exists?
scripts.BittyCameraBlocks.displayPortExists(~)
Get Viewport X
scripts.BittyCameraBlocks.getDisplayPortX(~)
Get Viewport Y
scripts.BittyCameraBlocks.getDisplayPortY(~)
Get Viewport Width
scripts.BittyCameraBlocks.getDisplayPortWidth(~)
Get Viewport Height
scripts.BittyCameraBlocks.getDisplayPortHeight(~)
Display
Setup
Set Display Area
scripts.BittyCameraBlocks.setGameAreaRect(~,~,~,~);
Set Display Area Position
scripts.BittyCameraBlocks.setGameAreaPosition(~,~);
Set Display Area Size
scripts.BittyCameraBlocks.setGameAreaSize(~,~);
Background
Set Background Color
scripts.BittyCameraBlocks.setBackgroundColorInt(~,~);
Set Background Image
scripts.BittyCameraBlocks.setBackgroundImage(~);
Clear Background Image
scripts.BittyCameraBlocks.clearBackgroundImage();
Has Background Image?
scripts.BittyCameraBlocks.hasBackgroundImage()
Properties
Get Display Area X
scripts.BittyCameraBlocks.getGameAreaX()
Get Display Area Y
scripts.BittyCameraBlocks.getGameAreaY()
Get Display Area Width
scripts.BittyCameraBlocks.getGameAreaWidth()
Get Display Area Height
scripts.BittyCameraBlocks.getGameAreaHeight()
Effects
Zoom
Set Camera Base Size
scripts.BittyCameraBlocks.setCameraBaseSize(~,~,~);
Set Zoom
scripts.BittyCameraBlocks.setCameraZoom(~,~);
Tween Zoom
scripts.BittyCameraBlocks.setCameraZoomAdvanced(~,~,~,~);
Get Camera Zoom
scripts.BittyCameraBlocks.getCameraZoom(~)
Reset Camera Zoom
scripts.BittyCameraBlocks.resetCameraZoom(~);
Screenshake
Shake Camera
scripts.BittyCameraBlocks.shakeCamera(~,~,~);
Shake Camera XY
scripts.BittyCameraBlocks.shakeCameraXY(~,~,~,~);
Stop Camera Shake
scripts.BittyCameraBlocks.stopCameraShake(~);
FAQs
These are the common first-time problems and quick fixes. Start here when something looks wrong before digging into the deep technical cave. The cave has bats. Probably Haxe bats.
Problem: Nothing appears, or the camera system acts like it does not exist.
Solution: Begin every scene with this block before using any camera, viewport, display, zoom, rotation, or screenshake blocks.
Problem: A camera was created, but it is not showing in the right place.
Solution: Make sure the camera is rendered to a viewport. Use this block after creating the camera and viewport.
Problem: The viewport is too large, too small, or off-screen.
Solution: Use Display Area units. If the Display Area is 960x540, a left-half viewport is 0, 0, 480, 540. Use the Viewport Tester for help.
Problem: The camera shows too much or too little of the world.
Solution: Change Camera Size, not Viewport Size. Camera Size controls how much world the camera sees before zoom.
Problem: Split-screen math feels wrong.
Solution: Split the Display Area, not the screen. For a 720x405 Display Area, left/right split uses 360x405 for each viewport.
Problem: Pixel art looks uneven or blurry.
Solution: Use camera sizes that scale evenly into the viewport. Example: 320x180 into 1920x1080 gives a clean 6x scale.
Problem: Zoom looks broken or crops more than expected.
Solution: Remember that zoom changes how much world the camera sees. Higher zoom means closer view. Set the camera base size first, then apply zoom.
Problem: The camera follows the wrong actor or crashes when centering.
Solution: Make sure the actor variable exists before using it in the block. In the Quick Start example, player means your own actor variable.
Problem: Actors disappear, die, or stop updating when outside Stencyl’s built-in camera view.
Solution: Check actor behaviors for off-screen cleanup, kill-when-off-camera logic, or visibility-based simulation changes. The Dynamic Camera System can show a different view than Stencyl’s default camera.
Problem: A viewport background or border does not appear.
Solution: Check that the viewport exists first and that alpha is above 0. For a fully visible color, use 100 percent alpha.
Problem: The display area is smaller than the screen and there is empty space around it.
Solution: That can be correct. The Display Area is where the camera output appears. Use background color or background image blocks to control what appears behind it.
Release Notes v 1.0
Version 1.0 is the first release-ready version of the bittyBIG Dynamic Camera System. It includes:
- custom cameras
- camera sizing
- actor centering
- target movement
- smoothing
- camera offsets
- grid-based movement
- camera presets
- zoom
- tweened zoom
- camera rotation
- screenshake
- camera transition checks
- viewport layout
- split-screen support
- picture-in-picture support
- viewport backgrounds
- viewport borders
- viewport transforms
- display area control
- background color support
- background image support
- unlocked cameras
- pixel-perfect display behavior
Development Trail
The Dynamic Camera System started with a simple practical problem: remove camera jitter from Stencyl projects while keeping the normal Stencyl workflow intact. My larger goal is to help put Stencyl on the map by proving it can power a polished, modern-feeling hit game when paired with the right tools.
Over time, that small fix grew into a full rendering and camera control pipeline.
The project still keeps Stencyl as the editor with the familiar block system, actor workflow, scene setup tool, and asset pipeline.
Version 1.0 is the first release-ready baseline: a flexible camera suite built from the original jitter-removal goal, now ready for real projects.
Planned Future Features
Cached Background Renderer
- Cache static background images so bittyBIG can draw them using its own camera and parallax math.
- Goal: reduce background pixel snapping caused by Stencyl's render path and give backgrounds smoother camera movement.
Cached Tile / World Renderer
- Cache static tile and world layers at scene load, then render from those cached images instead of relying on Stencyl's live layer redraw path.
- Goal: reduce dependency on Stencyl's built-in camera, improve consistency across viewports, and prepare the renderer for stronger HTML5 output.
Manual Rebuild Blocks
- Add blocks that let users manually rebuild cached backgrounds, tile layers, or world layers when something changes.
- Goal: support scenes where mostly-static layers occasionally need to update without forcing everything to stay live-rendered forever.
Dirty-Region Updates
- Update only the changed areas of cached layers instead of rebuilding the full cached image.
- Goal: support larger scenes and runtime edits with less redraw cost.
Animated / Dynamic Background Support
- Support animated backgrounds, dynamic background layers, and other background effects after the static cached background path is stable.
- Goal: keep the smooth bittyBIG-controlled background movement while still allowing more active scene presentation.
HTML5 Tile Seam Improvements
- Add rendering support for duplicated 1px tile borders or equivalent seam protection around cached tiles.
- Goal: reduce visible seams during smooth HTML5 camera movement without forcing the camera to snap.
Per-Viewport Debug Drawing
- Add optional debug drawing for each viewport and camera.
- Goal: make it easier to see camera bounds, viewport rectangles, centers, targets, offsets, and split-screen behavior while testing.
Renderer Independence
- Continue moving static rendering responsibilities into bittyBIG while keeping Stencyl as the editor, actor, block, scene, and asset workflow.
- Goal: let bittyBIG control runtime camera/output behavior more completely without abandoning the Stencyl creation pipeline.