Primative is my experiment with trying to create a rust baseed real time video graph engine, with the goal of building a reliable, user friendly, and open source frieldly cross platform environment for live code and creative tech. It's deployed on github pages! Check it out!
Primative, Adjective: Unaffected or little affected by civilizing influences; uncivilized. Simple; unsophisticated.
Primative, Fine arts, Noun: A naive or unschooled artist. An artist belonging to the early stage in the development of a style.
Primative, Mathematics, Noun: A function of which the derivative is a given function.
- Single crate (
src/lib.rsis shared;src/main.rsis the native entry,web_main()annotated with#[wasm_bindgen(start)]is the web entry). - winit 0.30
ApplicationHandlerfor the event loop on both targets. - wgpu 24, with the
glslfeature so naga can ingest#version 450GLSL and cross-compile it to MSL on Mac and SPIR-V → WebGPU in the browser. Thewebglfeature is enabled onwasm32so browsers without WebGPU still get a WebGL2 fallback. - Async device init is bridged across platforms with an
EventLoopProxy<UserEvent>: the wasm pathspawn_locals the future and postsStateReady(state)back to the event loop; the native path usespollster::block_on. - Fullscreen-triangle vertex shader + Mandelbrot fragment shader. Uniforms:
resolution,mouse(pixels),time. Mouse → NDC conversion handles the winit y-down / NDC y-up flip.
npm run dev:native # or, equivalently, `cargo run --release`One-liner — builds, serves, opens a browser tab:
npm run dev:webSet PORT=8001 (or any value) to override the default 8000; the script
falls through to the next free port if the requested one is in use.
Manual flow when you'd rather not have a browser tab pop:
./build-web.sh
cd web/dist && python3 -m http.server 8000
# open http://localhost:8000build-web.sh adds the wasm32-unknown-unknown rustup target and installs a
wasm-bindgen-cli whose version matches the wasm-bindgen crate in
Cargo.lock (the two must match exactly).
npm install # one-time
npx playwright install chromium # one-time
npm test # native + web + cross-platform diff
npm run test:native # cargo test --test headless
npm run test:web # rebuild wasm + Playwright screenshot
npm run test:cross # strict pixel-perfect cross-platform diffNative and web run the same three checks against their own platform's rendering pipeline:
- Pinned-baseline diff. A locked input (
time = 2.5,mouse_uv = (0.5, 0.5)) should produce a deterministic frame. Mean abs RGB diff must be ≤ 8/255 vstests/baseline.png(web, browser screenshot) ortests/baseline-native.png(native,render_offscreenreadback). Catches shader regressions and surface-format drift. - Mouse-API responsiveness. Image must change by ≥ 12/255 when the cursor moves — proves the mouse uniform is plumbed through.
- Error gate. Web fails on any
[error]/[pageerror]/[netfail]; native fails on any panic viacargo test. Mostly catches naga translation errors and validation panics the moment they appear.
Each platform has its own baseline. They render byte-identical pixels at
the same locked input — verified by npm run test:cross, which
strict-compares the native render_offscreen readback against the web
canvas screenshot and fails on any single pixel diff. Achieving zero diff
required two non-obvious fixes: configuring the surface with an sRGB view
format on web (Chrome only exposes non-sRGB storage formats), and
suppressing the browser-injected canvas focus outline before screenshot.
See CLAUDE.md for details.
Run UPDATE_BASELINE=1 npm test after intentional rendering changes and
commit both baselines. Diagnostics for the web runs land in tests/output/.
Cargo.toml
src/
lib.rs # shared app + State + ApplicationHandler
main.rs # native entry
render_graph.rs # Node trait + GlslNode + RenderGraph (TOML-loaded)
config/
graph.toml # graph definition incl. inline GLSL source per node
web/
index.html # canvas + module loader (carried into web/dist by build-web.sh)
build-web.sh # wasm32 → web/dist/ with version-matched wasm-bindgen-cli
tests/
screenshot.mjs # Playwright + pngjs harness
CLAUDE.md # operating notes for Claude Code (build commands, gotchas)
- wgpu must be ≥24. wgpu 22 sends the now-removed WebGPU spec field
maxInterStageShaderComponents, which modern Chromium rejects atrequestDevice. - The web canvas's drawing-buffer dimensions must be set in JS before
the wasm reads them.
index.html's inline script doesc.width = window.innerWidth; c.height = window.innerHeightbeforeawait init(). Without thiswinit::Window::inner_size()races browser layout and returns 0×0, which previously produced a 1×1 surface that CSS scaled up to look right but rendered only one fragment-shader sample.