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.
This is not a classic noise library. This is a new effort centered around software shader execution.
A pure-Ruby CPU implementation of the Noisemaker
shader engine — the Ruby port of noisemaker-for-cpu,
sibling to the Python
and Perl ports.
Effect kernels are transpiled directly from the upstream GLSL served by the shaders.noisedeck.app CDN (sha256-locked). They are not hand-maintained. A pure-Ruby GLSL ES 3.00 front end lexes, preprocesses, parses, and emits a Ruby kernel per shader pass. A float32-faithful runtime reproduces the reference engine's arithmetic:
- Float32 register rounding.
- Half-float render-target quantization.
- GLSL uint32 wraparound with bit-exact PCG hashing.
- Screen-space derivatives.
- GL texture sampling.
The bundle includes 210 catalog effects (295 kernels), transpiled from the
published engine 1.0.271 catalog with program GLSL taken from the pinned
Noisemaker a3d96b66 sources (recorded as glslRoot in the bundle
provenance). The parity harness
compares exact RGBA8 output with a pinned JavaScript reference. It uses 8×8
images, seed 1, time 0.25, small volume atlases, and identical explicit scenes
for particle consumers. These checks cover the catalog, not every parameter,
resolution, or animation. Iterated scenes also have separate integration tests.
This implementation is useful for offline rendering and running exported
Noisedeck compositions in Ruby without native extensions or a GPU. It is an
interpreter, and shader-heavy effects are slow: a warm 32×32 synth/curl render
allocated about 24 million objects and took several seconds on the development
machine with Ruby 4.0. Start at 32×32 before increasing resolution. Real-time
playback and large production renders are not its practical target.
Core stdlib only — no runtime gem dependencies. Ruby 3.2+. New installations should use a maintained Ruby: 3.4 or 4.0 (Ruby maintenance branches; 3.2 reached end of life on 2026-04-01 and stays supported here only as the compatibility floor).
gem build noisemaker-for-ruby.gemspec
gem install noisemaker-for-ruby-0.0.0.gemThe build also works from a source archive without a .git directory.
For Bundler, add gem "noisemaker-for-ruby", path: "/path/to/checkout" to your
Gemfile. Both require "noisemaker-for-ruby" and require "noisemaker_cpu" load
the engine. There are no runtime network requests.
Or run directly from a checkout: ruby -Ilib exe/noisemaker-rb ....
Discover effects and their parameters without reading generated code:
noisemaker-rb effects
noisemaker-rb effects filter/
noisemaker-rb describe synth/curlCLI examples use small images deliberately. The existing command defaults are
1024×1024 for generate and 512×512 for run and animate; pass dimensions
explicitly for an initial render.
noisemaker-rb generate synth/solid --width 64 --height 64 --param 'color=#4080c0' --filename solid.png
noisemaker-rb generate synth/curl --width 32 --height 32 --seed 1 --param scale=16 --filename curl.png
noisemaker-rb apply filter/lighting curl.png --filename lit.png
# Needs ffmpeg for MP4; saved PNGs are retained if ffmpeg is unavailable.
noisemaker-rb animate synth/solid --width 32 --height 32 --frame-count 4 --save-frames frames --filename solid.mp4
# Render a Polymorphic DSL program from stdin.
printf 'search synth, filter\nnoise(seed: 3, ridges: true).vignette().write(o0)\nrender(o0)\n' |
noisemaker-rb run --width 32 --height 32 --filename noise.pngImage input and output are PNG. MP4 encoding requires an ffmpeg executable
on PATH; the renderer itself has no external runtime dependencies.
Library:
require "noisemaker-for-ruby"
renderer = NoisemakerCpu::Renderer
surface = renderer.render_effect(
"synth/curl", { scale: 16, ridges: false }, nil,
width: 32, height: 32, seed: 1
)
File.binwrite("curl.png", NoisemakerCpu::PNG.encode_png(surface))
# A filter consumes a Surface through the inputs Hash. Surface parameters
# defaulting to inputTex, such as lighting's heightMap, use this image.
filtered = renderer.render_effect(
"filter/lighting", {}, { inputTex: surface }, width: 32, height: 32
)
File.binwrite("lit.png", NoisemakerCpu::PNG.encode_png(filtered))Parameter and input keys accept strings or symbols. Booleans accept
true/false, 1/0, and the corresponding CLI spellings yes/no or
on/off. Enum parameters accept the names or numeric values printed by
describe. Colors accept RGB(A) arrays or hex strings; vectors accept numeric
arrays or comma-separated CLI values. Invalid names and values raise
ArgumentError with the effect and parameter identified. Metadata slider
ranges are hints, not clamps; volumeSize and stateSize accept custom positive
sizes as well as their named presets.
Bind explicit surface overrides in the inputs Hash, for example
{ inputTex: surface, heightMap: another_surface }. Set heightMap: nil to
leave it unbound; the equivalent DSL argument is heightMap: none.
Use Renderer.render_dsl(program, width: 32, height: 32, seed: 1) for multi-effect
compositions, including particle and volume pipelines. Methods prefixed with
_ and generated kernel files are implementation details.
The vendored kernels + metadata under lib/noisemaker_cpu/bundle/ are
generated from the CDN at the exact engine version recorded in
bundle-lock.json (cached to .cdn-cache/, sha256-locked). A rebuild
reproduces the committed bundle byte for byte:
ruby scripts/build-bundle.rb --allTo move to a newer engine release, name it and accept the new sources:
NM_SHADER_VERSION=1.0.271 ruby scripts/build-bundle.rb --all --update-lock.
When the pinned upstream revision's GLSL runs ahead of the recorded CDN tag,
build against that exact source instead of a newer tag:
NM_GLSL_ROOT=<pinned noisemaker checkout> ruby scripts/build-bundle.rb --all --update-lock. Program GLSL is then read from the checkout's
shaders/effects/<id>/glsl/<program>.glsl — the same sources the pinned
oracle compiled — and the bundle provenance records glslRoot, while the
CDN tag continues to supply the effect catalog metadata.
Builds are staged and validated before replacing the installed bundle. Fetch,
compile, or lock-drift errors leave the previous bundle intact. An intentional
source update requires --update-lock; review both the generated changes and
the parity results before publishing. --only id,id builds a subset bundle,
so use --all when regenerating the shipped catalog.
bundle install
bundle exec rake testA standalone checkout runs unit, CLI, archive-installation, and regression
tests. Only external checks skip when their dependencies are unavailable:
JavaScript parity needs the pinned reference checkout and Node 22+, video
verification needs ffmpeg/ffprobe, and live CDN checks are opt-in through
NOISEMAKER_LIVE_CDN=1.
scripts/oracle-lock.json records the exact JavaScript repository revision
and source identity. Place that revision in a sibling noisemaker-for-cpu
checkout, or set NOISEMAKER_CPU_DIR to its path. A changed or dirty reference
is rejected; upgrading it requires an explicit lock update and parity review.
NOISEMAKER_REQUIRE_ORACLE=1 bundle exec rake test
bundle exec ruby scripts/parity.rb
# A focused check, useful during development:
bundle exec ruby scripts/parity.rb --only synth/curl,filter/lightingThe harness fails on any pixel difference, runtime failure, missing oracle,
or unknown selection. Every push runs the standalone tests on Ruby 3.2, 3.3,
3.4 and 4.0 on Linux and Ruby 4.0 on macOS, including archive installation
under Bundler and C-locale rendering. A weekly run (or a manual
workflow_dispatch) adds the integration tests against the pinned reference,
video frame counts, and scripts/parity-summary over all 210 reference
comparisons on Linux, plus the standalone tests and scripts/parity-summary on
Windows with Ruby 4.0; an export kit is released only after that run passes. scripts/release-gate.rb audits a complete scripts/parity-summary output against the release policy: every authority case reported exactly once, zero MISSING, strict, near and defer cases, the SKIP cases exactly the declared set and the FAIL cases exactly the accepted failures (both empty for this port — every case must render byte-exact), and the PARITY-SUMMARY counts agreeing with the per-case verdict lines. scripts/test runs the gate over the whole-port summary; the release-gating parity run invokes scripts/parity-summary directly, and scripts/parity-summary exits 0 only for the all-exact state the gate requires (every case executed byte-exact, nothing missing, strict, deferred, skipped or failing). Live CDN checks are separate from this
reproducible gate and may require NOISEMAKER_PERL_LOCK for cross-port lock checks.
The live compatibility report
is the current measured support: the tested source and reference 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.
MIT © Noise Factor LLC. See LICENSE.
