canvas

buffergeometry / drawrange

What this shows

Up to 1,000 particles drift inside a rotating box and connect with fading line segments when they come close, using setDrawRange to draw only the live part of preallocated point and line buffers.

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

Differences from three.js

  • EASEL has no blending modes, so the additive blending on the points, the connecting lines and the box outline becomes a plain overwrite: overlapping lines no longer add up to brighter crossings, and the points material uses layer 1 so the white dots stay on top of the lines that end on them.
  • EASEL point size is an integer pixel radius without size attenuation, so size 3 (a 3-pixel-wide square that ignores distance in three.js) becomes a radius-1 disc about 3 pixels wide.
  • Attribute.setUsage(DynamicDrawUsage) is a GPU buffer hint with no EASEL equivalent and is dropped; the attributes are republished with needsUpdate each frame as in three.js.
  • Points and lines are rasterized on the CPU without anti-aliasing, and lines are 1 pixel wide.
  • The particle update, the O(n^2) connection search and all drawn points and lines run on the CPU; a Bun stub-canvas run at 640x360 measured a median of about 7 to 16 ms per frame at the defaults and about 85 ms per frame with particleCount 1000 and minDistance 300.
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";


const particles = new EASEL.Geometry();
particles.setDrawRange(0, particleCount);
particles.setAttribute("position", new EASEL.Attribute(particlePositions, 3));
const pointCloud = new EASEL.Points(
  particles,
  new EASEL.PointsMaterial({ color: 0xffffff, size: 1, transparent: true }),
);

const geometry = new EASEL.Geometry();
geometry.setAttribute("position", new EASEL.Attribute(positions, 3));
geometry.setAttribute("color", new EASEL.Attribute(colors, 3));
geometry.setDrawRange(0, 0);
const linesMesh = new EASEL.LineSegments(
  geometry,
  new EASEL.LineMaterial({ vertexColors: true, transparent: true }),
);

linesMesh.geometry.setDrawRange(0, numConnected * 2);
linesMesh.geometry.getAttribute("position").needsUpdate = true;
linesMesh.geometry.getAttribute("color").needsUpdate = true;
renderer.prepare(scene, camera);
renderer.render(scene, camera);