canvas

buffergeometry

What this shows

160,000 random non-indexed triangles with flat face normals and position-based vertex colors fill a fogged cube that tumbles under ambient and directional light.

Side by side with the three.js r186 original: webgl_buffergeometry.html

Differences from three.js

  • MeshPhongMaterial becomes LambertMaterial with Gouraud (per-vertex) lighting, so the triangles have no white specular highlight (specular 0xffffff, shininess 250).
  • EASEL only reads RGB (itemSize 3) vertex colors and has no per-vertex alpha, so the random per-triangle alpha is dropped and the material is opaque instead of transparent; transparent: true would also turn off depth writes in EASEL and draw the triangles in submission order.
  • Attribute.onUpload is a GPU upload hook with no EASEL equivalent, so the typed arrays stay in memory.
  • Fog is sampled once per vertex from EASEL's 256-entry fog table instead of per pixel.
  • All 160,000 triangles are transformed, lit and rasterized on the CPU every frame; a Bun stub-canvas run at 640x360 measured a median of about 62 ms per frame (roughly 16 fps), above the 33 ms budget, and the count is kept to match three.js.
EASEL (Canvas2D)
This example needs a canvas-capable browser.

Loading scene…

three.js r186 (WebGL)
This example needs a canvas-capable browser.

Loading scene…

Loading scene…

Key API excerpt

import * as EASEL from "@xsyetopz/easel";


scene.fog = new EASEL.Fog({ color: 0x050505, near: 2000, far: 3500 });

const geometry = new EASEL.Geometry();
geometry.setAttribute("position", new EASEL.Attribute(new Float32Array(positions), 3));
geometry.setAttribute("normal", new EASEL.Attribute(new Float32Array(normals), 3));
geometry.setAttribute("color", new EASEL.Attribute(new Float32Array(colors), 3));
geometry.computeBoundingSphere();

const material = new EASEL.LambertMaterial({
  color: 0xd5d5d5,
  side: EASEL.Side.Double,
  vertexColors: true,
});
const mesh = new EASEL.Mesh(geometry, material);

scene.add(mesh);

mesh.rotation.x = time * 0.25;
mesh.rotation.y = time * 0.5;
renderer.prepare(scene, camera);
renderer.render(scene, camera);