Open source from Noise Factor · more projects
This package supports the "Export Shader Pipeline" feature in Noisedeck.app. The feature runs shader compositions on other platforms. Noise Factor derives this package from the upstream Noisemaker Engine project and tests it for pixel-level parity.
Noisemaker for Rust is a standalone CPU renderer for Noisemaker's generated
effect catalog. The Cargo package and command are named noisemaker-for-rust and noisemaker-rs. Rust code imports the library as noisemaker_cpu.
The renderer contains the catalog and typed shader IR at compile time. Normal library and CLI rendering does not invoke JavaScript, Node.js, Python, a browser, a GPU, the network, FFI, or a runtime shader compiler.
Rust 1.85 or newer is required.
cargo build --release
cargo install --path .Render one catalog effect with its defaults:
use std::collections::BTreeMap;
use noisemaker_cpu::{ParamValue, RenderOptions, Surface, render_effect};
let options = RenderOptions { width: 256, height: 256, ..Default::default() };
let image = render_effect(
"synth/gradient",
&BTreeMap::<String, ParamValue>::new(),
&BTreeMap::<String, Surface>::new(),
&options,
)?;
# Ok::<(), Box<dyn std::error::Error>>(())Or render a full DSL program:
use noisemaker_cpu::{CpuRenderer, RenderOptions};
let mut renderer = CpuRenderer::new()?;
let result = renderer.render(
"search synth,classicNoisedeck,filter; gradient().kaleido(sides: 8).vignette().write(o0)",
&RenderOptions { width: 256, height: 256, ..Default::default() },
)?;
# Ok::<(), Box<dyn std::error::Error>>(())Runnable versions are in examples/render_effect.rs and
examples/render_dsl.rs. They write to NOISEMAKER_OUTPUT when set and
otherwise choose an untracked temporary path.
The CLI has seven commands:
noisemaker-rs generate EFFECT [OPTIONS]
noisemaker-rs apply EFFECT INPUT.png [OPTIONS]
noisemaker-rs animate EFFECT [OPTIONS]
noisemaker-rs run [PROGRAM|-] [OPTIONS]
noisemaker-rs render PROGRAM|- [OPTIONS]
noisemaker-rs effect EFFECT [OPTIONS]
noisemaker-rs effects
generate renders an image generator. It also accepts random as the effect. This deterministically selects from the sorted eligible generator pool using --seed and prints the resolved ID first. apply renders an image filter or
mixer at the input image dimensions. run reads DSL from a file or standard
input, and render is its compatibility alias. effect constructs a bounded,
meaningful program for any catalog domain. effects prints all 210 sorted
ID<TAB>KIND records.
Common options are:
--width N output width (default 512)
--height N output height (default 512)
--time N finite render time (default 0)
--seed N signed deterministic seed (default 1)
--output PATH output file; --filename is an alias
--input PATH bind a PNG to imageTex and textTex
--texture NAME=PATH bind a named external PNG; repeatable
--param NAME=VALUE override a catalog parameter; repeatable, last value wins
--external-input FIX bind a deterministic external-input fixture (repeatable):
`midi`, `audio`, or `mesh`. Required by the reactive
(`synth/roll`, `synth/scope`, `synth/spectrum`) and mesh
(`render/meshLoader`, `render/meshRender`) effects; the
fixtures byte-match the CPU port's parity fixtures.
animate additionally accepts --frame-count (50), --fps (30), --speed
(1), and --save-frames DIR. It calls ffmpeg directly with literal arguments.
If ffmpeg is absent, saved frames remain usable. Without --save-frames, the command fails with installation guidance.
PNG and MP4 destinations are created through exclusive, unpredictable sibling temporary files and renamed only after successful encoding. A failed command preserves any pre-existing destination and removes only its own temporary file. Width times height is limited to 16,777,216 pixels.
The catalog contains all 210 CPU effects across image, volume, particle, renderer,
and loop domains, including the five upstream external-input effects that were
formerly excluded: the reactive synth/roll, synth/scope, and synth/spectrum
(MIDI/audio state bound as uniforms through --external-input midi or audio)
and the mesh render/meshLoader and render/meshRender (a parsed OBJ packed
into data textures and rasterized by a native CPU triangle-mesh draw adapter
matching the upstream drawMode: 'triangles' semantics: depth test LESS, CCW
back-face culling, Blinn-Phong shading, gamma 1/2.2). Their fixtures are the
deterministic constants shared with the CPU port's parity harness. The complete
generated inventory and the 460 non-null
compile-time choices are listed in docs/EFFECTS.md.
External-texture effects require --input or a matching --texture binding.
Volume work scales cubically with volumeSize. Iterated effects default to 60
steps, so large canvases can be expensive on a scalar CPU renderer. Particle filters are meaningful inside a pointsEmit/state/pointsRender chain. The effect command constructs that owner path automatically. Loop markers must be
paired around the repeated chain.
Coordinates are normalized with the origin at the lower left inside shader
execution while Surface and PNG rows are stored top-down. Colors are linear
floating-point RGBA internally and clamp/round to RGBA8 during PNG encoding.
Overlay texture generation is cached by a bounded least-recently-used CPU
cache. RenderOptions::one_shot selects Initial or Ready one-shot semantics. Ready is the public default.
Four programs use complete native fragment replacements for semantics that the
generic typed IR cannot represent directly. Snow uses an exact-key float32
semantic adapter to preserve the JavaScript CPU port's operation boundaries.
CRT remains on typed IR but selects one exact-key builtin-semantic adapter for its reduced-turn float32 sine. This matches the JavaScript CPU port without changing sin for any other shader. Temporal Aberration preserves the JavaScript factory's current true-branch assignment quirk behind its exact program key. Ordinary typed-IR conditional assignments retain their standard behavior.
The canonical hash_uint(uint) helper follows the JavaScript CPU compiler's
body routing at its exact mangled function target: a murmur-style finalizer
body keeps the source-compatibility hash, while the LCG-seeded mix body (the
points-pipeline agent kernels) executes the authority's LCG sequence.
Differently named user functions continue to execute their typed-IR bodies.
Compact CSL syntax is deliberately not implemented. Use the documented chained DSL, whose parser provides source locations and catalog-aware validation.
The checked-in bundle is generated from locked Noisemaker metadata and shader sources. Verify canonical generated files and the generated effect reference:
PYTHONDONTWRITEBYTECODE=1 python3 scripts/generate_bundle.py --checkSupplying --source PATH rebuilds from a source tree containing
metadata.json, bundle-lock.json, and effects.json or effects/. Existing shader hashes are immutable. There is no update-lock mode. The packaged
maintainer generator includes its complete scripts/transpiler dependency.
To move to a new upstream release, fetch every eligible effect with
NM_SHADER_VERSION=<release> through scripts/transpiler/cdn.py, which caches
effects/<id>.json under scripts/transpiler/.cdn-cache/<release>/. Build the
source tree from that cache (keeping only the programs the catalog's passes
name), a metadata.json that is src/generated/catalog.json without
paramNames and paramAliases plus any changed definitions, and a copy of
src/generated/bundle-lock.json whose hashes are updated for the programs that
intentionally changed. Take scripts/upstream-param-contract.json from the CPU
port's upstream-snapshot.js at the same upstream revision.
Cross-language maintenance parity compares one shared DSL program through the
built Rust and JavaScript CLIs, at 8×8 pixels unless --size says otherwise. It writes exactly one sorted record for every catalog effect and requires exact RGBA8 bytes (zero tolerance). It reports every unsupported interface with a stable reason. It treats timeouts or render errors as failures:
python3 scripts/parity.py \
--rust target/release/noisemaker-rs \
--js ../noisemaker-for-cpu/bin/noisemaker-cpu.jsUse --only ID, --size N, --timeout SECONDS, and --json PATH for focused or
machine-readable runs. --sweep extends the run with a bounded non-default
parameter sweep and stateful multi-frame sequences: every effect's default
render is joined by at most two one-parameter variants (deterministically
derived from the catalog metadata) and, for iterated, particle, loop,
reactive, and mesh effects, three frames of the default program at the 0.1/0.4/0.7
time sequence. Build the sequence example with the same toolchain, profile, and
target directory as the Rust CLI, then pass both binary paths explicitly:
cargo build --locked --release --bin noisemaker-rs --example parity_sequence
python3 scripts/parity.py \
--rust target/release/noisemaker-rs \
--rust-sequence target/release/examples/parity_sequence \
--js ../noisemaker-for-cpu/bin/noisemaker-cpu.js \
--sweepFrame cases reuse one renderer per side and feed each frame's floating-point
output through imageTex/textTex into the next frame's media/blendMode
chain. This measures explicit prior-output feedback; each renderer call recreates
its internal particle and feedback resources. Each case is one sorted record
(ID, ID#param:NAME, ID#frame:N) and the sweep report states expected,
executed, byte-exact, and failed counts at tolerance 0. JavaScript is an
offline test oracle only. It is never a production runtime dependency.
On every push, CI checks formatting, lints, the 1.85 minimum, the generator, and
the test suite without the catalog smoke tests. A push that changes src/**,
the Cargo manifests, or scripts/parity.py also runs the Linux cross-port
parity against the pinned CPU port revision. The catalog smoke tests (every
default program and every compile-time choice) and the Windows cross-port
parity run weekly and on manual dispatch. Only a weekly or manually dispatched
run releases the export kit, after its Linux parity passes.
The live compatibility report
is the current measured support: the tested source and oracle revisions, the
platforms, and the parity counts. The
issues labelled gap
are the current qualification limits. Both are GitHub issues in this
repository and are updated as results change.
See CONTRIBUTING.md for the contribution policy and the complete local verification commands, and the Code of Conduct for participation expectations. Report suspected vulnerabilities privately as described in SECURITY.md, not in a public issue.
Code is available under the MIT License. See TRADEMARK.md for trademark use.
