A browser-based 3D fractal renderer — pure WebGL ray marching with real-time parameter controls and a full mesh/image export pipeline.
pnpm --filter @workspace/fractal-engine run dev— start the fractal engine (auto-wired to thefractal-engine: webworkflow)pnpm run typecheck— full typecheck across all packagespnpm --filter @workspace/fractal-engine run typecheck— typecheck the fractal engine onlypnpm --filter @workspace/fractal-engine run test:e2e— run Playwright E2E tests (requires browsers installed)
No environment variables or database required — the app is entirely client-side.
- pnpm workspaces, Node.js 24, TypeScript 5.9
- Frontend: Vite + vanilla TypeScript (no React in the engine core)
- Rendering: pure WebGL 1 raymarcher (no Three.js)
- UI: vanilla DOM + custom CSS (
engine.css) - Export: CPU-side marching cubes → OBJ / PLY / STL / PNG / WebP
artifacts/fractal-engine/src/
main.ts — entry point, wires everything together
state.ts — global engine state (S object)
renderer.ts — WebGL raymarcher (real-time preview)
controls.ts — all UI event bindings and syncSliders()
shaders.ts — GLSL vertex + fragment shaders (inline strings)
engine.css — all engine-specific styles
export/
marchingCubes.ts — CPU field evaluation + Marching Cubes triangulation
exportStudio.ts — full mesh export pipeline (weld, Taubin smooth, normals, OBJ/PLY/STL)
imageExport.ts — offscreen WebGL render → PNG / WebP download
foldConstants.ts — FOLD_RANGE / FOLD_ISO constants shared across export
tables.ts — MC_EDGES / MC_TRIS lookup tables
artifacts/fractal-engine/tests/e2e/
controls.spec.ts — preset switching, button groups, sliders
export-studio.spec.ts — mesh generation + OBJ/PLY/STL download validation
image-export.spec.ts — PNG/WebP buttons + BUG-6 code audit
playwright.config.ts — Playwright config (SwiftShader for headless WebGL)
- No framework in the engine core. All rendering and UI is vanilla TypeScript + DOM. React/Radix are only present in
lib/packages inherited from the monorepo scaffold; the fractal engine does not use them. - CPU marching cubes for export. The real-time view is a GLSL raymarcher, but mesh export re-evaluates the SDF in JavaScript so export works without GPU access.
- Taubin smoothing, not Laplacian. Pure Laplacian shrinks the mesh. Taubin two-step (λ shrink → μ inflate, μ = −λ/(1−0.1λ)) preserves volume.
- Weld tolerance is range-adaptive.
weldVertices()usesmax(5e-6, FOLD_RANGE[ft] × 2e-6)so large-scale fractals don't miss duplicates. - Post-weld normals always recomputed. Welding remaps vertex indices; copying pre-weld gradient normals after weld produces corrupted shading. Standard/High/Cinematic always call
recomputeNormals().
Six fractal presets (Pure Menger, Box-B, Kleinian, Lattice, Tetra, Deep-∞) with real-time parameter sliders for iterations, scale, offsets, fold type, Julia constants, blend, and rotation. Export Studio generates production-quality meshes (DRAFT 32³ through CINEMATIC 128³) with optional Taubin smoothing, vertex welding, triplanar/spherical UV maps, and vertex colors. Image export renders hi-res PNG (1×/2×/4×) and WebP screenshots using the full matcap shading pipeline.
- Keep the engine core free of React and framework dependencies.
- Do not run
pnpm devat the workspace root — each artifact needsPORTandBASE_PATHfrom the workflow config. Userestart_workflowor the preview pane instead. console.errorinrenderer.tsis intentional — those are WebGL shader compile/link error reporters, not debug noise.- Image export requires WebGL —
buildOffscreenGL()alerts "Could not create offscreen WebGL context" in headless environments. Mesh export (marching cubes) is pure CPU and works everywhere. - Typecheck the engine with
tsc --noEmit, notbuild—buildneedsPORT/BASE_PATHfrom the workflow env.
- See the
pnpm-workspaceskill for workspace structure and TypeScript project references. foldConstants.tsis the single source of truth forFOLD_RANGEandFOLD_ISOvalues.- STL binary format: 80-byte header + 4-byte LE uint32 (triangle count) +
triCount × 50bytes.