proof-engine 0.2.1

Real-time graphics from math: glyphs and particles moved by ODEs, strange attractors and force fields, drawn with HDR bloom on OpenGL.
Documentation

Proof Engine is a Rust library for making moving pictures out of math: you write the equations, it draws what they do, in real time, with bloom and HDR light.

Install

You want to Do this
Use it in your own Rust program cargo add proof-engine
Watch the demos first git clone https://github.com/Mattbusel/proof-engine && cd proof-engine && cargo run --release --example galaxy
Read the API docs.rs/proof-engine

You need stable Rust and a GPU with OpenGL 3.3 or newer.

  • Windows: nothing else.
  • macOS: nothing else. macOS stops at OpenGL 4.1, so apotheosis (which uses 4.3 compute shaders) will not run there; every other demo does.
  • Linux: the audio backend needs the ALSA headers: sudo apt install libasound2-dev pkg-config (Debian/Ubuntu) or sudo dnf install alsa-lib-devel (Fedora).

The first build compiles the whole engine and takes a few minutes. Always use --release; a debug build is far too slow for tens of thousands of particles a frame.

Use it in 3 steps

1. Make a project and add the engine.

cargo new lorenz-demo && cd lorenz-demo

cargo add proof-engine

2. Replace src/main.rs with this. 5,000 points start on one short line and follow the Lorenz equations. (It is also in the repo as examples/quickstart.rs.)

use proof_engine::math::attractors::rk4_step;
use proof_engine::prelude::*;
use proof_engine::render::ui_layer::UiParticle;

fn main() {
    let mut engine = ProofEngine::new(EngineConfig::default());

    // 5,000 points in a line 2 units long, each 0.0004 from the next.
    let mut points: Vec<Vec3> = (0..5000)
        .map(|i| Vec3::new(1.0 + i as f32 * 4e-4, 1.0, 1.0))
        .collect();

    engine.run_ui(move |engine, dt| {
        // Advance every point along the Lorenz equations.
        for p in points.iter_mut() {
            *p = rk4_step(AttractorType::Lorenz, *p, dt);
        }
        // Draw them: x across, z up, centred in the window.
        let (w, h) = engine.render_size();
        let (cx, cy, s) = (w as f32 / 2.0, h as f32 / 2.0, h as f32 / 60.0);
        let color = Vec4::new(0.5, 1.2, 1.6, 1.0);
        let dots = points
            .iter()
            .map(|p| UiParticle::new(cx + p.x * s, cy - (p.z - 25.0) * s, 3.0, 3.0, '●', color))
            .collect();
        engine.ui.draw_particles(dots);
    });
}

3. Run it.

cargo run --release

A window opens. For about ten seconds the points travel together as one short streak. Then chaos pulls them apart, and by twenty seconds they have drawn the Lorenz butterfly on their own. Close the window to quit.

Results

These are real frames from that program, written by the engine itself (PROOF_HIDDEN=1 PROOF_FIXED_DT=60 PROOF_SHOT=..., see Capture frames), cropped to the centre:

The same idea at a larger scale, one command each:

strange_attractors math_rain
Seven chaotic systems, 1,500 RK4-integrated points each. Colour is speed along the flow. A hundred columns at speeds from abs(sin(0.13 c)); each column's symbols flicker by its own logistic map.
lorenz sky
40,000 points on the Lorenz attractor, drawn into the HDR pass so overlaps add up to real light. Every sky cell is a Rayleigh and Mie scattering integral, recomputed every frame as the sun moves.

The sky is an integral

The sky example divides the sky into 120 by 56 cells. For each cell, every frame, nishita_sky::compute_sky_color integrates Rayleigh and Mie single scattering along the view ray and along a second ray toward the sun. The blue at noon, the orange band at sunset and the dark teal after it all come out of the same integral as the sun moves. The mountains are sums of sines, lit by the sky just above the horizon. Space pauses, Up/Down change the speed of the day.

Forty thousand points, one equation

Every point in the lorenz example is a state (x, y, z) advanced each frame by the engine's own RK4 integrator. Nobody drew the two wings; that is where the equations send the points. Colour is speed along the flow, and the points are drawn into the HDR world pass, so overlapping points add up to real light before bloom and the tonemap.

use proof_engine::math::attractors::rk4_step;

for p in points.iter_mut() {
    for _ in 0..SUBSTEPS {
        *p = rk4_step(AttractorType::Lorenz, *p, h);
    }
}

What it does

Area What is there
Math functions as animation Lorenz, Rossler, Chen, Halvorsen, Aizawa, Thomas and Dadras attractors; sine, Perlin noise, logistic map, Collatz, golden spiral, Lissajous, Mandelbrot escape, spring-damper systems. Any glyph can have a life_function that drives its position or color.
Composable force fields Gravity, vortex, electromagnetic, strange attractor, shockwave, tidal, flow, magnetic dipole, entropy and damping, with linear, inverse-square, exponential or Gaussian falloff.
Particle-built entities Held together by force cohesion, with HP-linked binding strength.
OpenGL 3.3 HDR renderer glutin, winit and glow; instanced glyph rendering, half-float scene buffers, bloom, ACES, chromatic aberration, film grain, vignette, scanlines and motion blur.
Physics 2D rigid bodies with SAT collision, mass-spring soft bodies, Eulerian fluid, constraints and joints.
Audio 48 kHz synthesis (rodio, cpal), ADSR, FM, music-theory helpers (scales, chords, progressions), stereo panning and reverb.
Scripting A custom bytecode VM with lexer, parser and compiler, closures and tables.
Procedural generation Tectonics, erosion, climate, biomes, rivers, caves, settlements, history, language and quest generation, plus ecology models (Lotka-Volterra, SIR).
Proof Editor An egui scene editor for placing glyphs, force fields and entities, with an inspector, hierarchy, post-FX presets, undo/redo and JSON scenes.

The source tree also contains modules for more advanced lighting (a sparse voxel octree GI cone tracer, Nishita sky scattering, tiled and deferred lighting, volumetric fog, a wgpu backend). The Nishita sky model drives the sky example; the others exist as code but are not yet connected to any demo.

Demos

Every demo is cargo run --release --example <name>. Close the window (or press Esc where noted) to quit.

Example What you see
sky A day passing over a mountain range. Every sky cell is the Nishita Rayleigh + Mie scattering integral for its view direction, recomputed each frame. Space pauses, Up/Down change speed, Esc quits.
lorenz 40,000 points on the Lorenz attractor, integrated with RK4 and coloured by speed. Space pauses, Left/Right turn the view, Esc quits.
galaxy About 3,000 glyphs on four spiral arms, each on its own orbit, with a hot core, red outer arms and drifting nebula dust. The camera circles slowly. Esc quits.
strange_attractors Lorenz, Rossler, Chen, Halvorsen, Aizawa, Thomas and Dadras side by side, 1,500 points each. Space pauses, Esc quits.
math_rain Digital rain made of mathematical symbols; column speeds from a sine, symbol flicker from a logistic map. Esc quits.
quickstart The program from Use it in 3 steps: 5,000 points that chaos tears apart into the Lorenz butterfly.
convergence Two particle-built fighters in a circular arena with an orbiting camera; combat loops forever and hits knock matter loose.
supernova A star pulses, collapses under a gravity field, explodes into debris and settles into a Lorenz-attractor nebula.
hello_glyph The smallest program: one breathing @ and a gravity field.
playground Interactive sandbox: place glyphs, fields and entities with the mouse, cycle attractors and palettes.
colossus GPU density entities: millions of particles derived in the vertex shader from sixteen bones. Needs a strong GPU.
apotheosis A particle-rendered character built on signed distance fields, about 10.8 million GPU particles. Needs OpenGL 4.3 and a strong GPU.

Also: chaos_field, particle_demo, force_fields, amorphous_entity, particle_entity, full_combat, heartbeat, showcase and sculptor.

Capture frames from any program

Any program built on the engine, every example included, can write its own frames to disk without a line of code changing. Frames are read back off the GPU after post-processing, so what lands on disk is exactly what the pipeline drew. The images on this page were made this way.

PROOF_HIDDEN=1 PROOF_FIXED_DT=60 PROOF_SHOT='frames/f_{n}.bmp' \

PROOF_SHOT_AT=560 PROOF_SHOT_EVERY=2 PROOF_SHOT_COUNT=240 \

cargo run --release --example sky

Variable Meaning
PROOF_SHOT Output path. {n} becomes the capture index, zero padded to four digits. Files are 24-bit BMP.
PROOF_SHOT_AT Frame of the first capture (default 120).
PROOF_SHOT_COUNT / PROOF_SHOT_EVERY How many captures, and how many frames apart (defaults 1 and 1).
PROOF_SHOT_KEEP 1 keeps running after the last capture instead of exiting.
PROOF_FIXED_DT Step the simulation at this many frames per simulated second, so a sequence plays back at true speed however long each frame took.
PROOF_HIDDEN 1 creates the window hidden and unfocused, so capturing does not interrupt whoever is using the machine.
PROOF_WINDOW Override the window size, as WIDTHxHEIGHT.

The screen pipeline

The scene buffers are half-float, so a few hundred thousand overlapping emissive particles accumulate real light instead of clipping at white. The composite is the one place the range comes down, through ACES.

Two UI passes. engine.ui routes every command to a pass. Particle clouds and filled rectangles default to the world pass, which is painted into the HDR buffer before post-processing; text, outlines, bars and sprites default to the HUD pass, painted sharp on top afterwards. A panel splits: fill to the world, border to the HUD. ui.begin_world(), ui.begin_hud() and ui.end_pass() override the default for a run of commands. A game that draws its whole picture as screen-space matter gets bloom, grade, lens and grain on all of it, and a readable interface over that.

What the composite does, in order: shockwave refraction, heat haze, barrel lens, chromatic aberration, unsharp mask, floor reflection, exposure, screen-space indirect light (matter near a lit thing is lit by it), bloom, halation, light shafts, lens flare, flash, ACES tonemap, lift/gain grade, tint, contrast, saturation, vignette, shadow-weighted grain, ordered dither, scanlines. Every standing parameter is a field on RenderConfig; the moments are on engine.fx.

engine.fx (ScreenFx). Fire-and-forget effects that decay on their own: shockwave(x, y, strength), flash(color, strength), light_shaft_at(x, y, strength) or auto_shafts = true to stream from whatever is brightest on screen, reflect_at(y, strength, fade) for a glossy floor, and haze for heat shimmer. Coordinates are UI pixels.

Lights and shadows. engine.fx.light(x, y, radius, color, intensity) and engine.fx.ambient. The glyph pass writes an occluder buffer (matter, never floors or panel fills); the light pass marches shadows through it from every light and the composite multiplies the scene by the result. Emissive matter lights itself. config.persistence keeps a decaying copy of last frame's scene under this one, for motion trails.

GPU density entities. engine.init_gpu_density(n) and engine.queue_gpu_density_entity(data): sixteen bones become millions of particles derived in the vertex shader from the instance index, with breathing, jitter and matter that comes loose as hp falls. Nothing per particle ever leaves the GPU. See examples/colossus.rs.

Sound. A MathAudioSource carries a pitch envelope, a second partial, a noise mix, biquad or comb filters, drive, a reverb send, and a start delay, and the output thread honours all of it, with separate music and effects buses, ducking, a master reverb and a soft limiter. A blow is a crack, a thud and a ring.

Also: render_scale renders the scene at a fraction of the window and upsamples; fxaa runs a real FXAA 3.11 pass between the composite and the HUD; shake_pixels moves the world pass with camera trauma while the HUD stays put; vsync waits for the display.

Proof Editor

Proof Editor

Download proof-editor.exe (Windows) from the releases page, or build it:

cd proof-engine/editor

cargo run --release

Key Action
Click viewport Place with current tool
WASD / arrows Pan camera
V / G / P / F / E / X Select, move, place glyph, place force field, place entity, particle burst
Shift+Click Multi-select
Ctrl+C / Ctrl+V Copy / paste
Ctrl+Z / Ctrl+Y Undo / redo
Ctrl+S / Ctrl+O / Ctrl+N Save / load / new scene
Delete Remove selection
Space Screen shake
F1 Help

Architecture

Roughly 660,000 lines of Rust across the engine (src/), the editor (editor/) and the examples. Benchmarks: cargo bench (Criterion: particle_bench, glyph_bench).

Module Contents
render OpenGL pipeline, post-FX, shader graph
math attractors, fields, curves, noise, springs, MathFunction evaluation
glyph, particle, entity the core primitives and their pools
physics rigid body, soft body, fluid, constraints
audio, dsp synthesis, music theory, effects, spatial audio
ecs archetype ECS with generational IDs
scripting lexer, parser, compiler, bytecode VM
terrain, worldgen, ecology, narrative procedural generation
game boss AI, cloth, debris, achievements
nishita_sky physical sky scattering, used by the sky example
svogi, volumetric_fog, tiled_lighting, wgpu_backend advanced lighting, not yet wired into the demos

examples/apotheosis.rs is a standalone showcase of a particle-rendered character built on signed distance fields instead of meshes. A 26-bone capsule skeleton is blended with Inigo Quilez's polynomial smooth minimum, particles are importance-sampled onto the SDF shell, and normals, ambient occlusion and subsurface thickness are all computed from the SDF itself. On top of that it layers per-material shading (Schlick Fresnel, Kajiya-Kay hair and fabric specular, thin-film iridescence), a strand-based hair renderer, inertial lag for loose materials, and a post stack including TAA jitter, spectral bloom, god rays, depth of field bokeh and ACES tonemapping. It targets about 10.8 million GPU particles.

Related

chaos-rpg is a roguelike whose graphical frontend runs on Proof Engine. CHAOS_RPG_API_CONTRACT.md documents what the engine has to support for it.

Status

Early (0.2) and moving fast. The public API is not stable and some subsystems are further along than others. CI builds every target and runs the unit, integration and doc tests on Linux, and builds the examples on Windows and macOS. About 4,800 library unit tests pass; the ones that do not yet are listed by name in ci/known-failing-tests.txt, and each one fixed is a line deleted from that file. Contributions: see CONTRIBUTING.md.

License

MIT, see LICENSE.

Hire the author

Need this kind of engineering on your product? I take on a small number of client builds: LLM features, iOS apps and performance work, fixed price. Services and pricing · Email · LinkedIn