DEV Community

Cover image for # 🎥 Mastering the Camera System — Part 2: The Fake Camera and When NOT to Sync
Kehinde Owolabi
Kehinde Owolabi

Posted on

# 🎥 Mastering the Camera System — Part 2: The Fake Camera and When NOT to Sync

🎥 Mastering the Camera System — Part 2: The Fake Camera and When NOT to Sync

Everything You Need to Know About the Fake Camera and Why Syncing Can Ruin Your Performance


📖 Introduction

In Part 1, we covered the basics of camera follow, shake, and zoom. But there's another camera in Limn Engine that most developers don't fully understand — the fake camera.

The fake camera is part of Limn Engine's dual-canvas rendering system. It controls the background layer (tilemaps, static decorations, and background images). Understanding when and how to use it is critical for performance.

The most common mistake? Syncing the fake camera with the main camera. In this article, we'll explain why that's a problem and how to fix it.


🎯 What We'll Cover

  1. What is the fake camera?
  2. How the dual-canvas system works
  3. The problem with syncing cameras
  4. When to sync (and when NOT to)
  5. The correct approach: resize, don't sync
  6. Practical examples
  7. Common mistakes and how to avoid them

🎮 What Is the Fake Camera?

The fake camera is attached to the fake canvas — an offscreen buffer that caches static content.

Canvas Purpose Camera
Main canvas Dynamic content (player, enemies, particles) display.camera
Fake canvas Static content (tilemaps, backgrounds, decorations) fake.camera

The fake camera controls how the static background is rendered. It's part of the dual-canvas pipeline that gives Limn Engine its performance.


🏗️ How the Dual-Canvas System Works

Before we dive into the fake camera, let's understand how the dual-canvas system works.

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                    MAIN CANVAS                          │   │
│   │                                                         │   │
│   │   Dynamic content: player, enemies, particles          │   │
│   │   Camera: display.camera                               │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                    FAKE CANVAS                          │   │
│   │                    (offscreen)                         │   │
│   │                                                         │   │
│   │   Static content: tilemaps, backgrounds, decorations   │   │
│   │   Camera: fake.camera                                  │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   Both cameras are independent!                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

What's happening here:

  1. The main canvas is what you see on screen. It contains everything that moves — the player, enemies, bullets, and particles. Its camera is called display.camera.

  2. The fake canvas is hidden. It's an offscreen buffer that contains everything that doesn't move — tilemaps, backgrounds, and static decorations. Its camera is called fake.camera.

  3. The two canvases are rendered separately and combined on screen. The fake canvas is drawn first (as the background), then the main canvas is drawn on top of it.

  4. Each canvas has its own independent camera. The fake camera is completely separate from the main camera.

The important thing to understand: The fake canvas is designed to be drawn once and then reused. Moving it every frame defeats its purpose.


🔴 The Problem: Syncing Cameras

Many developers assume they need to sync the fake camera with the main camera. Let's look at the wrong way first.

❌ The Wrong Way: Syncing Cameras

function update(dt) {
    // Step 1: Make the main camera follow the player
    display.camera.follow(player, true);

    // Step 2: Copy the main camera's position to the fake camera
    fake.camera.x = display.camera.x;   // ❌ This is a mistake!
    fake.camera.y = display.camera.y;   // ❌ This is a mistake!
}
Enter fullscreen mode Exit fullscreen mode

What's happening here:

  1. The main camera follows the player. This is correct.

  2. We copy the main camera's position to the fake camera. This is the mistake.

  3. Now the fake camera moves with the main camera every frame.

Why This Is a Problem

Issue What Happens Why It's Bad
Defeats caching The fake canvas is supposed to be static. Moving it every frame breaks the cache. The fake canvas was designed to be drawn once and reused. Moving it forces it to be redrawn every frame.
Performance hit Instead of one draw call for the whole background, the engine has to re-render everything. The whole point of the fake canvas is to reduce draw calls. Syncing increases them back to normal levels.
Wastes CPU The fake canvas was designed to be drawn once. Syncing forces it to be redrawn every frame. This defeats the performance benefits of the dual-canvas system.
No benefit The fake canvas can be sized to the world, so moving it is unnecessary. There's a better way that doesn't hurt performance.

What You Should Do Instead

Instead of moving the fake canvas, resize it to match the world size. Let's look at the correct way.


✅ The Correct Approach: Resize, Don't Sync

Step 1: Set the World Size

Here, you're going to define how big your game world is. This tells the engine the boundaries of your world so the camera knows how far it can move.

// Define how big your game world is
display.camera.worldWidth = 2000;
display.camera.worldHeight = 2000;
Enter fullscreen mode Exit fullscreen mode

What this code does:

  • display.camera.worldWidth = 2000; — Sets the width of your game world to 2000 pixels. The camera will never show anything beyond this width.
  • display.camera.worldHeight = 2000; — Sets the height of your game world to 2000 pixels. The camera will never show anything beyond this height.

Why it matters: The world size determines how big the fake canvas needs to be. If you have a world that's 2000x2000, the fake canvas needs to be 2000x2000 to hold it all. If you don't set this, the camera defaults to 1000x1000, and anything outside that will be cut off.

When to use this: At the very beginning of your game setup, before you render anything to the fake canvas.


Step 2: Resize the Fake Canvas

Now you're going to make the fake canvas exactly the size of your entire game world. This way, it can hold the whole map without needing to move.

// Resize the fake canvas to match the world
fake.canvas.width = display.camera.worldWidth;   // Now 2000
fake.canvas.height = display.camera.worldHeight; // Now 2000
Enter fullscreen mode Exit fullscreen mode

What this code does:

  • fake.canvas.width = display.camera.worldWidth; — Sets the fake canvas width to match your world width (2000 pixels).
  • fake.canvas.height = display.camera.worldHeight; — Sets the fake canvas height to match your world height (2000 pixels).

Why it matters: Because the fake canvas is now the full world size, it contains everything. The main camera just shows a window into it. The fake canvas never needs to move. If you don't do this, the fake canvas will be smaller than your world, and parts of your background will be cut off.

When to use this: Right after setting display.camera.worldWidth/Height, before rendering anything to the fake canvas.


Step 3: Render Static Content Once

Now you're going to draw all your static content — tilemaps, backgrounds, decorations — onto the fake canvas. This happens only once.

// Render the tilemap to the fake canvas
display.tileMap();
display.tileFace.show();

// Add static decorations to the fake canvas
fake.add(tree);
fake.add(decoration);
Enter fullscreen mode Exit fullscreen mode

What this code does:

  • display.tileMap(); — Creates the tilemap object that will hold your level data.
  • display.tileFace.show(); — Renders all the tiles onto the fake canvas. This draws your entire level.
  • fake.add(tree); — Adds a tree decoration to the fake canvas.
  • fake.add(decoration); — Adds another decoration to the fake canvas.

Why it matters: After this, the fake canvas contains a complete image of the entire static world. It never needs to be redrawn. If you don't do this, your static content will be drawn on the main canvas, which means it will be redrawn every frame — killing performance.

When to use this: After resizing the fake canvas, before the game loop starts.


Step 4: Update Only the Main Camera

In your game loop, you only update the main camera. The fake canvas stays exactly where it is — it contains the entire world.

function update(dt) {
    // Main camera follows the player
    display.camera.follow(player, true);

    // ✅ NO fake.camera sync needed!
    // The fake canvas is already the full world size
}
Enter fullscreen mode Exit fullscreen mode

What this code does:

  • display.camera.follow(player, true); — Makes the main camera follow the player with smooth interpolation.
  • No fake camera code is needed here.

Why it works: The main camera is like a window that moves over the fake canvas. The fake canvas itself never moves. If you sync the cameras, you'll defeat the caching and hurt performance.

When to use this: Every frame in your update loop.


📊 Visual Example

Wrong Approach (Syncing)

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Frame 1: Move fake camera                                    │
│   Frame 2: Move fake camera                                    │
│   Frame 3: Move fake camera                                    │
│   ...                                                          │
│                                                                 │
│   Result: Fake canvas redrawn every frame.                   │
│           Caching is defeated.                                 │
│           Performance drops.                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Correct Approach (Resizing)

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Frame 1: Fake canvas contains entire world                  │
│   Frame 2: Main camera moves over it                          │
│   Frame 3: Main camera moves over it                          │
│   ...                                                          │
│                                                                 │
│   Result: Fake canvas drawn ONCE.                            │
│           Main camera shows a window into it.                 │
│           Performance is optimal.                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

✅ When to Sync (and When NOT To)

Situation Should You Sync? Why
Static world ❌ No Resize the fake canvas once
Tilemap ❌ No The fake canvas can hold the whole map
Background image ❌ No One image, one draw call
Decorations ❌ No They don't move
Scrolling world ❌ No Resize to world size, let the main camera scroll
Camera shake ❌ No Shake the main camera only
Parallax layers ⚠️ Sometimes Only if you're implementing parallax manually
Dynamic backgrounds ⚠️ Sometimes Only if the background actually changes

🧠 Key Takeaways

Concept What It Means
Fake canvas Offscreen buffer for static content
Fake camera Controls how the fake canvas is rendered
Syncing Copying display.camera to fake.camera
The problem Syncing defeats caching and hurts performance
The solution Resize the fake canvas to match the world
The rule Render static content once, move the main camera

⚠️ Common Mistakes

Mistake Why It's Wrong How to Fix
Syncing cameras Defeats caching and hurts performance Resize the fake canvas instead
Moving the fake canvas It should be static Set it once and leave it
Redrawing the fake canvas Wastes CPU cycles Use display.once correctly
Forgetting to set world size Fake canvas is too small Set display.camera.worldWidth/Height

🎯 The One-Line Summary

"The fake camera is NOT for syncing — it's for caching. Resize the fake canvas to match your world, and let the main camera do the moving."


Draw your game into existence — with a camera system that performs. 🎥🚀


🔗 Resources


Top comments (0)