What it does for you
Generated art usually ends as pixels nobody can inspect. studio-engine keeps the whole recipe: the shader, a sound graph, a motion timeline, the criteria it was judged against, every refinement step and a receipt. You can re-render it, steer it over a local HTTP API, or render PNG frames with no GPU. It runs on the Python standard library alone.
- One expression, every outputThe same formula becomes the GLSL shader, the scored samples, the timeline and the synth graph.
- Frames without a GPUA software rasterizer writes deterministic PNG frames, each hashed into the receipt.
- Tampering is refusedThe rasterizer re-hashes the shipped expression and will not draw a mismatch.
- Ten generatorsSeven fields and three point sets, each one table entry.
Source: README.md at 24cd0f4 (version 0.3.0)
Watch
No concept film fits this tool closely yet. The walkthrough below covers it in text, with real commands and output.
Video walkthrough: coming with the next release.
How it works, one step at a time
Scroll, or use the step buttons. Every line is output from studio-engine at commit 24cd0f4, from python -m studio_engine --render-frames 7 gyroid and from the engine called in Python with an empty novelty corpus.
- 01
A seed and a generator
Pick the gyroid with seed 7. The seed drives a fixed integer hash, so it sets the starting parameters and a six-colour palette in OKLCh. The gyroid starts at frequency 8.88 and z 0.66.
Source: studio_engine/engine.py,
run - 02
Score, then refine the weakest axis
Each step samples the field on a 20 by 20 grid and scores four axes: clean frequency, contrast, complexity and novelty. Cohesion is their harmonic mean. Refinement moves one parameter at a time toward higher cohesion. The frequency jumps to 10, a whole number, then z rises to 0.95, and cohesion climbs from 0.7983 to 0.8922.
- 03
Short of the target, and an outside check
Convergence needs cohesion of 0.9 with every axis at or above the floor. At 0.8922 the run stops as best effort, unconverged. A separate structural-fitness check from coherence-membrane then measures a deviation of 0.108 against a tolerance of 0.4 and verifies the world. Without that package installed, this line reads unverifiable.
Source: studio_engine/certify.py
- 04
One expression, three outputs
The field sin(10u)cos(10v) + sin(10v)cos(10t) + sin(10t)cos(10u) is held as a frozen tree. It is emitted as the GLSL
field()body, parsed back, and required to evaluate equal to the original within 1e-6. Swept over t it gives a timeline with a period of 0.628 seconds whose loop has no jump. Sonified, it becomes a six-harmonic additive sine synth at 373.7 Hz.Source: studio_engine/strand/glsl.py, studio_engine/temporal.py, studio_engine/strand/webaudio.py
- 05
Frames, and a tampered program
--render-framesrasterizes eight PNG frames across the loop period and writes a manifest binding each frame's SHA-256 to the expression hash. Before drawing, the rasterizer rebuilds the expression from the shipped tree and re-hashes it. Pick a program in the panel: changing one constant from 10 to 11 makes the hash disagree, and the render is refused.Source: studio_engine/raster_renderer.py
- 06
Same input, same world, for a fixed corpus
Novelty is measured against a corpus of everything made before, and the corpus grows with every run. With the corpus cleared, seed 7 gives world 1e1312cf579e9deb every time. Run it again without clearing and the new gyroid is a near copy of the last one: novelty falls to 0.08 and the result changes.
Source: studio_engine/corpus.py; README.md, "Why it matters"
Walkthrough
Install it, run it once, then use the main feature. Each command below is real, and so is its output.
Get it
Clone it. Python 3.10 or newer, no install step.
$ git clone https://github.com/HarperZ9/studio-engine && cd studio-engineFirst run: a world with frames
Generate the gyroid at seed 7 and render PNG frames with no GPU.
$ python -m studio_engine --render-frames 7 gyroid world 1e1312cf579e9deb | 'Gyroid #7' steps=8 converged=False final_score=0.8922 render=glsl-fragment expr_sha=528f4a81a82a6ea2 timeline period=0.628319 continuity=verified rendered 8 PNG frame(s) -> studio-out/frames-7/ (+ frames.json)Open the chamber
Start the API, then open
handoff/reference-chamber.htmlto see the shader and hear the synth.$ python -m studio_engine.server 8777
Output from studio-engine at 24cd0f4 with Python 3.12 on Windows. The unittest suite reported 195 tests passing. Ids depend on the novelty corpus, which the CLI keeps at studio_engine/_corpus.json.
What it does not do
- The criteria are coarse aesthetic axes. A score is a measured read of them and makes no claim about beauty.
- The engine emits shader programs and synth graphs as data. Browsers, GPUs and audio hosts play them.
- Version 0.3.0; the API may still change before 1.0. The native GPU renderer is a separate project, and the bridge says so when it is absent.
Source: README.md at 24cd0f4, "Scope and maturity" and "Why it matters"
Check what stuck
Answer each one in your head before you open it.
Why does a second run of seed 7 give a different world?
Novelty is scored against a corpus that grew with the first run.
What does the rasterizer check before drawing a frame?
It rebuilds the expression from the shipped tree, re-hashes it, and refuses on a mismatch.
What happens to the GLSL before it ships?
It is parsed back and must evaluate equal to the original expression within 1e-6.