Skip to content

Rendering

The framework provides two renderers — PixiRenderer for WebGL/WebGPU and CanvasRenderer for Canvas 2D — plus GameLoop for the update/render cycle.

PixiRenderer

The recommended renderer for new games. Uses PixiJS 8 for hardware-accelerated rendering.

typescript
import { PixiRenderer, GameLoop } from "quantum-forge/rendering";

class MyRenderer extends PixiRenderer {
  protected draw(state: GameState) {
    // this.graphics is a PixiJS Graphics object — cleared each frame
    this.graphics.circle(state.player.x, state.player.y, 10).fill("#fff");

    // drawText manages PixiJS Text objects by key
    this.drawText("score", `Score: ${state.score}`, 10, 10, {
      fill: "#fff",
      fontSize: 16,
    });

    // Sprites, containers, etc. via this.app (the PixiJS Application)
  }
}

const renderer = new MyRenderer({
  canvas: document.getElementById("game-canvas") as HTMLCanvasElement,
  backgroundColor: 0x000000,
  logger,
});

// PixiJS requires async init
await renderer.init();

WARNING

await renderer.init() must complete before starting the game loop. PixiJS initializes WebGL/WebGPU context asynchronously.

CanvasRenderer

For simpler games or Canvas 2D use cases:

typescript
import { CanvasRenderer, GameLoop } from "quantum-forge/rendering";

class MyRenderer extends CanvasRenderer {
  render(state: GameState) {
    this.clear("#000");
    drawCircle(this.ctx, state.player.x, state.player.y, 10, "#fff");
  }
}

CanvasRenderer provides this.ctx (a CanvasRenderingContext2D) and helper methods like clear().

GameLoop

Manages the update/render cycle at a target frame rate:

typescript
const loop = new GameLoop({
  update: (deltaTime: number) => {
    input.poll();
    // Update game logic
  },
  render: () => {
    renderer.render(engine.getState());
  },
  targetFps: 60,
  logger,
});

loop.start();
// loop.stop();

deltaTime is in seconds. At 60 FPS, deltaTime ≈ 0.0167.

Rendering from State

The cardinal rule: renderers derive visuals from state. They never compute, cache, or decide.

typescript
// ✅ Correct: read state, draw
protected draw(state: GameState) {
  for (const enemy of state.enemies) {
    this.graphics.circle(enemy.x, enemy.y, enemy.radius).fill(enemy.color);
  }
}

// ❌ Wrong: computing in renderer
protected draw(state: GameState) {
  const screenX = this.worldToScreen(enemy.x); // don't compute here
  this.cachedPositions[enemy.id] = screenX;     // don't cache
}

Quantum Visualization

For quantum objects, read probability from the registry and map to visual properties:

typescript
protected draw(state: GameState) {
  for (const ball of state.balls) {
    const alpha = ball.isQuantum ? ball.existenceProbability : 1.0;
    this.graphics.circle(ball.x, ball.y, ball.radius).fill({
      color: ball.color,
      alpha,
    });
  }
}

The existenceProbability comes from the engine state (set by the engine's helpers using registry.getExistenceProbability()), not queried directly by the renderer.

Powered by Quantum Forge