EASEL.js
A Canvas2D software renderer for the browser with a THREE.js-style scene graph, CPU rasterization, and examples built for people searching beyond WebGL.
Why people land here
The strongest entry points are concrete search intents: a THREE.js-style API for Canvas2D, a browser CPU rasterizer, and a clear answer for people who typed `easeljs` but actually want a modern playground for software-rendered 3D.
Write familiar code
The API mirrors THREE.js where that improves approachability. Scene, Mesh, Camera, Light, Material, and Geometry stay familiar while the renderer swaps GPU assumptions for a readable CPU pipeline.
import * as EASEL from "@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);Install
Available on npm and JSR. One package, zero runtime dependencies.
npmjs.com
npm install @xsyetopz/easel
jsr.io
npx jsr add @xsyetopz/easel
Features
THREE.js-Compatible Mental Model
Same scene graph vocabulary: Scene, Mesh, Camera, Light, Material, Geometry. The migration path is legible instead of theoretical.
CPU Software Renderer
Every pixel is drawn by the CPU using a painter-sorted scanline rasterizer. No WebGL, no shader setup, no GPU dependency.
Canvas2D Output
Renders to a standard HTML5 Canvas via ImageData, making the renderer easy to inspect, record, and embed in browser tooling.
Zero Dependencies
No runtime dependencies. One import, no transitive packages to audit.
TypeScript
Typed API surface with JSDoc-backed definitions, checkJs compatibility, and editor autocomplete out of the box.
Retro Pipeline Constraints
Rendering constraints modeled after old software pipelines: affine UV, HSL16 color, integer coordinates, and flat or Gouraud shading.
Coming from THREE.js?
Name mapping
| THREE.js | EASEL.js | Reason |
|---|---|---|
Object3D | Node | Scene graph node |
BufferGeometry | Geometry | No GPU buffers |
WebGLRenderer | Renderer | Single renderer |
MeshBasicMaterial | BasicMaterial | "Mesh" prefix redundant |
MeshLambertMaterial | LambertMaterial | Same |
MeshToonMaterial | ToonMaterial | Same |
AnimationMixer | Animator | Plays clips |
KeyframeTrack | Track | All tracks are keyframe |
Key differences
- Constructor takes an options object, not positional arguments
- No dispose() needed for most objects (no GPU resources)
- Materials omit the "Mesh" prefix (LambertMaterial, not MeshLambertMaterial)
- Renderer outputs to Canvas2D, not WebGL
- Lighting is flat or Gouraud only (no per-pixel shading)
- Textures are 128x128 max with nearest-neighbor sampling
- Opacity uses 9 discrete steps (0-8), not continuous alpha
Looking for EaselJS / CreateJS?
EASEL.js is not the original CreateJS EaselJS package.
If you searched for `easeljs`, you probably expected a Canvas library. This project targets a narrower, stranger niche: software-rendered 3D scenes, painter sorting, scanline fill, and Canvas2D output without WebGL.
- Scene graph and geometry instead of a 2D stage graph
- CPU rasterization instead of sprite composition
- Docs and examples aimed at rendering pipelines
Why RuneTek 3?
RuneTek 3 is the Java-based software rasterizer that powers OldSchool RuneScape.
Like RuneTek, EASEL.js computes every pixel on the CPU with no GPU acceleration.
Architectural constraints inherited from the RuneTek 3 engine:
- 128x128 max texture with nearest-neighbor sampling
- 9-step discrete opacity (0-8), not continuous alpha
- Affine UV mapping -- no perspective correction
- HSL16 packed color (6H/3S/7L) with precomputed RGB LUT
- Integer screen coordinates with round-half truncation
- Tile-radius fog with hard cutoff
- Painter's algorithm with depth buffer for residual overlap
Rendering Pipeline
Each frame flows through six stages. The painter's algorithm sorts back-to-front, then a scanline rasterizer fills triangles with affine UV mapping and per-vertex shading.
Walk the scene graph, collect visible meshes and lights.
Discard objects beyond the fog far plane.
Sort faces back-to-front by tile distance and layer.
Compute per-vertex lighting from all active lights.
Scanline-fill triangles with affine UV and shading.
Write pixels to ImageData and upload to the canvas.