Software 3D for browser Canvas2D.

EASEL.js gives JavaScript teams a renderer they can read: THREE-style objects, CPU rasterization, and deterministic canvas pixels.

Canvas2D output

Frames land in ImageData on a standard canvas. No WebGL state or GPU lifecycle.

Pipeline stages you can inspect

Traversal, ordering, light bake, scanline fill, CPU depth tests, and framebuffer upload stay readable.

Plain scene objects

Scene, Mesh, Camera, Light, Material, and Geometry stay readable while the renderer remains CPU-only.

Renderer limits are explicit

Camera tradeoffs, affine UVs, sorted transparency, and discrete opacity are documented.

Small package

Zero runtime dependencies, typed surface, npm and JSR distribution.

Built for constrained renderers.

The renderer keeps the hard parts visible: stage timing, draw order, raster output, and framebuffer state.

Baked lighting

Flat and Gouraud shading happen before rasterization.

Camera tradeoffs

Orthographic keeps affine UVs exact; perspective is available with visible texture warp.

Depth is explicit

Opaque fragments use a CPU depth buffer; transparent materials still need sorted order.

Discrete blending

Opacity uses fixed steps instead of fragile float alpha paths.

Install it. Read the pipeline.

Docs, source snippets, and rendered examples stay crawlable. Live editing belongs in a separate app surface.

npmjs.com

bun install @xsyetopz/easel

jsr.io

bunx jsr add @xsyetopz/easel
const EASEL = require("@xsyetopz/easel");

const renderer = new EASEL.Renderer({ canvas, width: 800, height: 600 });
const scene = new EASEL.Scene();
const camera = new EASEL.PerspectiveCamera({ fov: 45, aspect: 4 / 3 });
camera.position.set(0, 2, 5);

scene.add(new EASEL.AmbientLight(0xffffff, 0.4));

const box = new EASEL.Mesh(
  new EASEL.BoxGeometry(1, 1, 1),
  new EASEL.LambertMaterial({ color: 0xff4444 }),
);
scene.add(box);

renderer.render(scene, camera);
See full example →