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.
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:
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:
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.
// ✅ 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:
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.