Rendering API
import {
PixiRenderer,
CanvasRenderer,
GameLoop,
DeltaTimeTracker,
Camera,
} from "quantum-forge-engine/rendering";pixi.js is an optional peer dependency. Install it before using PixiRenderer.
PixiRenderer
WebGL/WebGPU renderer using PixiJS 8. Abstract: subclass it and implement draw().
Constructor
new PixiRenderer(config: {
canvas: HTMLCanvasElement;
width?: number; // default: canvas.clientWidth, else 800
height?: number; // default: canvas.clientHeight, else 600
backgroundColor?: number | string; // default: 0x000000
maxDpr?: number; // default: 2
antialias?: boolean; // default: true
logger?: LoggerInterface;
})Methods
| Method | Description |
|---|---|
init(): Promise<void> | Create the WebGL/WebGPU renderer. Await it before rendering |
render(state, deltaTime?) | Clears graphics, calls draw(), hides unused text, renders the stage |
draw(state, deltaTime?) | protected abstract. Implement it to draw a frame |
drawText(key, content, x, y, style?) | protected. Managed Text object, returns it |
setBackground(color: number | string) | Change the background color |
resize(cssWidth, cssHeight) | Resize the renderer |
getCanvas(): HTMLCanvasElement | The canvas passed to the constructor |
getMetrics(): PixiRenderMetrics | Size, DPR, and text pool counts |
destroy() | Release PixiJS resources |
Protected members
| Member | Type | Description |
|---|---|---|
stage | Container | Root container. Add persistent display objects (sprites, containers) here |
graphics | Graphics | Cleared before every draw(). Use for immediate-mode shapes |
logger | LoggerInterface | undefined | The configured logger |
There is no app property. PixiRenderer builds a renderer with autoDetectRenderer() and owns the stage itself, so scene-graph work goes through this.stage.
Metrics
interface PixiRenderMetrics {
cssWidth: number;
cssHeight: number;
canvasWidth: number;
canvasHeight: number;
dpr: number;
textsActive: number;
textsPooled: number;
}Override
class MyRenderer extends PixiRenderer {
protected draw(state: GameState) {
this.graphics.circle(state.player.x, state.player.y, 10).fill("#fff");
this.drawText("score", `Score: ${state.score}`, 10, 10, { fontSize: 16 });
}
}drawText(key, content, x, y, style?)
Keeps one PixiJS Text object per key. The first call creates it, later calls update the string and position. Text not drawn in a frame is hidden rather than destroyed, so repeated draws do not churn textures.
CanvasRenderer
Canvas 2D renderer. Abstract: subclass it and implement render().
Constructor
new CanvasRenderer(config: {
canvas: HTMLCanvasElement;
width?: number; // with height, triggers an initial resize()
height?: number;
maxDpr?: number; // default: Infinity
logger?: LoggerInterface;
})Methods
| Method | Description |
|---|---|
render(state, deltaTime?) | abstract. Implement it to draw a frame |
clear(color?: string) | Clear the canvas, optionally filling with a color |
resize(cssWidth, cssHeight) | Size the backing store to the CSS size times DPR, capped by maxDpr |
getMetrics(): RenderMetrics | CSS size, canvas size, and DPR |
getCanvas(): HTMLCanvasElement | The canvas element |
getContext(): CanvasRenderingContext2D | The 2D context |
canvas, ctx, logger, and maxDpr are protected, so subclasses can use this.ctx directly.
Canvas helpers
Free functions exported alongside the renderer:
drawCircle(ctx, x, y, radius, color);
drawRect(ctx, x, y, width, height, color);
drawLine(ctx, x1, y1, x2, y2, color, width?); // width default: 1
drawText(ctx, text, x, y, color, font?, align?); // font default: "16px monospace"
lerp(a, b, t): number;
clamp(value, min, max): number;GameLoop
requestAnimationFrame loop with delta time tracking. targetFps sets the fixed update timestep (dt = 1 / targetFps); rendering still runs on every animation frame.
Constructor
new GameLoop(config: {
update: (deltaTime: number) => void; // dt in seconds
render: () => void;
targetFps?: number; // default: 60
logger?: LoggerInterface;
})Methods
| Method | Description |
|---|---|
start() | Begin the loop |
stop() | Cancel the pending frame and stop |
isRunning(): boolean | Whether the loop is running |
getFps(): number | Measured frames per second |
deltaTime is in seconds. At 60 FPS, deltaTime ≈ 0.0167.
DeltaTimeTracker
Delta timing without the loop, for games that drive their own frames.
const tracker = new DeltaTimeTracker();
const dt = tracker.getDelta(); // seconds since the last call, capped at 0.1
tracker.reset();Camera
World-space to screen-space transforms for 2D games. x and y are the top-left corner of the view, width and height are the viewport size. There is no zoom.
Constructor
new Camera({
x?: number; // default: 0
y?: number; // default: 0
width: number; // required
height: number; // required
bounds?: { minX: number; minY: number; maxX: number; maxY: number };
})bounds clamps the camera so the view stays inside the world rectangle.
Methods
| Method | Description |
|---|---|
moveTo(x, y) | Place the top-left corner |
moveBy(dx, dy) | Offset the current position |
centerOn(x, y) | Center the view on a world point |
follow(target: Point, smoothing?) | Track a { x, y } target. smoothing default 0.1, use 0 for a hard snap |
worldToScreen(worldX, worldY): Point | World to screen coordinates |
screenToWorld(screenX, screenY): Point | Screen to world coordinates |
isVisible(worldX, worldY, margin?) | Whether a point is inside the view |
isRectVisible(x, y, width, height, margin?) | Whether a rectangle overlaps the view |
applyTransform(ctx) | Translate a 2D context by the camera offset |
resetTransform(ctx) | Undo applyTransform |
applyToContainer(container) | Position a PixiJS container by the camera offset |
resize(width, height) | Change the viewport size |
const camera = new Camera({ width: 800, height: 600 });
camera.follow(state.player);
const screen = camera.worldToScreen(enemy.x, enemy.y);
const world = camera.screenToWorld(pointer.x, pointer.y);