scenic-draft
v0.1.0
Published
Describe a background and a CSG object tree, get a progressively path-traced image on a WebGL2 canvas.
Maintainers
Readme
scenic-draft
Describe a background and a CSG object tree, get a progressively path-traced image on a WebGL2 canvas. The scene is compiled into a GLSL signed-distance shader (all parameters baked in as literals), then a progressive path tracer accumulates one sample per pixel per frame.
Usage
import {
gradient,
paint,
plane,
render,
smoothUnion,
sphere,
sun,
torus,
translate,
union,
} from 'scenic-draft';
const handle = render(canvas, {
background: gradient([0.02, 0.03, 0.05], [0.85, 0.9, 1.0], {
sun: sun([-0.5, 0.8, -0.3], [6, 5, 4]),
}),
scene: union(
plane([0, 1, 0], -1.2),
smoothUnion(
0.4,
sphere(1.2),
translate(
paint(torus(1.6, 0.35), { color: [0.85, 0.2, 0.2], roughness: 0.6, metallic: 0 }),
[0, 0.9, 0],
),
),
),
});
// When the canvas goes away (e.g. React effect cleanup):
handle.stop();The environment is the only light source, so give the background some
contrast (or a sun) if the image looks flat.
Scene nodes
- Primitives —
sphere(r),box(halfExtents, rounding?),torus(major, minor),cylinder(r, halfHeight),capsule(r, halfHeight),plane(normal, offset?) - Transforms —
translate(node, [x, y, z]),rotateX/Y/Z(node, radians),scale(node, factor) - CSG —
union(...),intersect(...),subtract(base, ...cuts)andsmoothUnion(k, ...),smoothIntersect(k, ...),smoothSubtract(k, base, ...cuts)wherekis the blend radius - Modifiers —
shell(node, thickness)hollows a solid,grow(node, amount)inflates/rounds - Materials —
paint(node, { color, roughness?, metallic? })applies to the whole subtree; the innermostpaintwins. Unpainted primitives are gold.
Nodes are plain data (see SceneNode in types.ts), so trees can be built,
serialised or generated without the helper functions.
Rendering
render(canvas, spec, options?) sizes the canvas backing store, compiles the
shader and starts a requestAnimationFrame loop. Options: size (default
1024), maxFrames (1200), bounces (6), seed (fixed seed for reproducible
accumulation). It returns { stop(), frames }.
buildShader(spec, bounces?) exposes the generated GLSL for inspection and
testing — it is a pure function of the spec.
Development
This package lives inside a pnpm workspace
package (src/scenic-draft), but has no dependencies on the surrounding app.
pnpm test— vitest over the codegen (pure functions, no WebGL required)pnpm build— assembles the publishable package into the gitignoredpackage/folder:tscemits JS +.d.tstopackage/dist, andscripts/build-package.mjswrites a publish-only manifest and READMEpnpm release— test, build, thennpm publish ./package
The demo page (/scenic-draft in the site) lives outside the package in
src/pages/ScenicDraft.tsx.
