Raw2D is a low-level 2D rendering engine for JavaScript and TypeScript.
It is browser-first, Canvas-first, WebGL2-ready, and built around small isolated modules. Objects store scene data and transform behavior. Renderers draw those objects.
Public docs: https://raw2d.com/doc and markdown reference: https://raw2d.com/readme
Raw2D is not trying to replace PixiJS or Phaser. Those are powerful high-level engines.
Raw2D is for developers who want:
- explicit control over the render pipeline
- small focused classes
- readable TypeScript internals
- Canvas as the complete reference renderer
- WebGL2 batching without hiding too much
- tooling-friendly APIs for editors, visual tools, and engine experiments
The public pipeline names should stay understandable and testable:
Scene -> RenderList -> Batcher -> Buffer -> Shader -> DrawCall
Performance matters, but the project identity is control, modularity, readable diagnostics, and transparent pipeline steps.
Use the umbrella package first:
npm install raw2dFocused packages are available for advanced users:
npm install raw2d-core raw2d-canvas raw2d-webgl raw2d-sprite raw2d-text raw2d-interaction raw2d-react raw2d-mcpPackage split rule: app examples should import from raw2d, engine-builder examples may import focused packages, and all Raw2D packages are versioned together so releases stay easy to audit.
CDN usage:
<script src="https://cdn.jsdelivr.net/npm/raw2d@latest/dist/raw2d.umd.cjs"></script>
<script>
const { BasicMaterial, Camera2D, Canvas, Rect, Scene } = Raw2D;
</script><canvas id="raw2d-canvas"></canvas>import { BasicMaterial, Camera2D, Canvas, Rect, Scene } from "raw2d";
const canvasElement = document.querySelector<HTMLCanvasElement>("#raw2d-canvas");
if (!canvasElement) {
throw new Error("Canvas element not found.");
}
const renderer = new Canvas({
canvas: canvasElement,
width: 800,
height: 600,
backgroundColor: "#10141c"
});
const scene = new Scene();
const camera = new Camera2D();
const rect = new Rect({
x: 100,
y: 80,
width: 180,
height: 100,
material: new BasicMaterial({ fillColor: "#35c2ff" })
});
scene.add(rect);
renderer.render(scene, camera);Use Canvas when you want the most complete and easiest renderer path.
import { Canvas } from "raw2d";
const renderer = new Canvas({ canvas: canvasElement });
renderer.render(scene, camera);Use WebGLRenderer2D when your scene uses supported objects and you want explicit batching, packed-atlas texture reuse, static render runs, opt-in safe sprite sorting, and performance stats.
import { WebGLRenderer2D } from "raw2d";
const renderer = new WebGLRenderer2D({ canvas: canvasElement });
renderer.render(scene, camera);
const stats = renderer.getStats();
console.log(stats.drawCalls);
console.log(stats.textureBinds);
console.log(stats.spriteTextureBindReduction);Both renderers implement the shared Renderer2DLike contract:
import type { Renderer2DLike } from "raw2d";
function drawFrame(renderer: Renderer2DLike): void {
renderer.render(scene, camera);
}import { Camera2D, Canvas, Scene, Sprite, TextureLoader } from "raw2d";
const renderer = new Canvas({ canvas: canvasElement, backgroundColor: "#10141c" });
const scene = new Scene();
const camera = new Camera2D();
const texture = await new TextureLoader().load("/sprite.png");
scene.add(new Sprite({
texture,
x: 120,
y: 80,
width: 128,
height: 128,
origin: "center"
}));
renderer.render(scene, camera);Use TextureAtlas, TextureAtlasPacker, createSpriteFromAtlas, or AssetGroupLoader packAtlas when many sprites should share one texture. In WebGL this helps reduce texture binds, and packWithStats() shows atlas occupancy.
Interaction lives in focused modules, not inside the renderer.
import { InteractionController, SelectionManager } from "raw2d";
const selection = new SelectionManager();
const interaction = new InteractionController({
canvas: canvasElement,
scene,
camera,
selection,
onChange: () => renderer.render(scene, camera)
});
interaction.enableSelection();
interaction.enableDrag();
interaction.enableResize();raw2d-mcp is the automation package for agents, docs generators, and project tooling. It works with scene JSON, validates unknown input, inspects renderer hints, generates Canvas/WebGL examples, and returns explicit visual-check command plans.
import {
addRaw2DSceneObject,
createRaw2DSceneJson,
generateRaw2DCanvasExample,
validateRaw2DScene
} from "raw2d-mcp";
const document = addRaw2DSceneObject({
document: createRaw2DSceneJson(),
object: { type: "rect", id: "card", width: 120, height: 80 }
});
const result = validateRaw2DScene({ document });
const example = generateRaw2DCanvasExample({ document });
console.log(result.valid);
console.log(example.code);packages/
core/ raw2d-core
canvas/ raw2d-canvas
webgl/ raw2d-webgl
sprite/ raw2d-sprite
text/ raw2d-text
interaction/ raw2d-interaction
effects/ raw2d-effects
raw2d/ umbrella package
Rules that shape the codebase:
- TypeScript strict mode
- no external rendering library
- no React dependency in core packages
- each module keeps its own
*.type.ts - object classes do not contain Canvas/WebGL drawing logic
- renderers own drawing
- files stay small and focused
npm install
npm run devDocs run locally at:
http://localhost:5174/doc
Examples run at http://localhost:5174/examples/canvas-basic/ and the other examples/*/ paths.
Quality checks: npm test, npm run build:docs, and npm run pack:check.
WebGL2 renderer, batch rendering, texture atlas, object pooling, dirty matrix updates, culling, typed array buffers, and static/dynamic batches.
Raw2D is licensed under the Apache License 2.0.
Copyright 2026 Aditya Nandlal
Redistributions must keep the LICENSE and NOTICE files. The Raw2D name and project identity are project marks; do not use them to imply that a fork, package, product, or service is official unless you have permission.