A creative coding framework for real-time audio-visual work with hot-reloadable C++ chains. Clean, consistent API designed for both manual coding and AI-assisted development.
- Audio-Visual Parity - Audio and visuals are equal peers in code. Native synthesis, sequencing, and effects—no external plugins needed
- Hot Reload - Edit your C++ code and see changes instantly without restarting
- WebGPU Backend - Modern GPU API via wgpu-native (Metal on macOS, Vulkan/DX12 elsewhere)
- Chain-Based Architecture - Connect operators to build audio-visual pipelines
- Addon System - Modular design with automatic dependency discovery
- State Preservation - Feedback loops and animations survive hot reloads
- LLM-Friendly - Designed for AI-assisted development (see below)
From top-left: Feedback spirals, Kaleidoscope mirror effect, 3D PBR globe, Depth of field, GPU particles, Retro CRT simulation, Candy-style animation, Flow field visualization
- CMake 3.20+
- C++17 compiler (Clang, GCC, or MSVC)
- macOS, Windows, or Linux
git clone https://github.com/seethroughlab/vivid.git
cd vivid
cmake -B build && cmake --build build./build/bin/vivid examples/2d-effects/chain-basicsPress F to toggle fullscreen, Tab to view chain visualizer, Esc to quit.
Create a chain.cpp file:
#include <vivid/vivid.h>
#include <vivid/effects/effects.h>
using namespace vivid;
using namespace vivid::effects;
void setup(Context& ctx) {
auto& chain = ctx.chain();
// Add operators and configure properties
auto& noise = chain.add<Noise>("noise");
noise.scale = 4.0f;
noise.speed = 0.5f;
noise.octaves = 4;
auto& hsv = chain.add<HSV>("color");
hsv.input(&noise); // Connect via pointer
hsv.hueShift = 0.6f;
hsv.saturation = 0.8f;
chain.output("color");
}
void update(Context& ctx) {
// Parameter tweaks go here (optional)
}
VIVID_CHAIN(setup, update)Run it:
./build/bin/vivid path/to/your/projectEdit your code while it's running - changes apply automatically.
- setup() is called once on load and on each hot-reload
- update() is called every frame
- The core automatically calls
chain.init()after setup andchain.process()after update - Operator state (like Feedback buffers, video playback position) is preserved across hot-reloads
Noise- Fractal noise (Perlin, Simplex, Worley, Value)SolidColor- Constant colorRamp- Linear gradientGradient- Multi-mode gradients (linear, radial, angular, diamond)Shape- SDF shapes (circle, rect, triangle, star, polygon)LFO- Oscillators (sine, triangle, saw, square)Image- Load images from disk
Blur- Gaussian blurTransform- Scale, rotate, translateHSV- Hue/saturation/value adjustmentBrightness- Brightness, contrast, gammaMirror- Axis mirroring and kaleidoscopeDisplace- Texture-based distortionEdge- Sobel edge detectionPixelate- Mosaic effectChromaticAberration- RGB separationBloom- Glow effectTile- Texture tilingFeedback- Frame feedback with decay
Dither- Ordered dithering (Bayer 2x2, 4x4, 8x8)Quantize- Color palette reductionScanlines- CRT-style linesCRTEffect- Full CRT simulation (curvature, vignette, phosphor)Downsample- Low-res pixelated look
Math- Mathematical operations (add, multiply, clamp, remap, etc.)Logic- Comparison and logic (greater than, in range, toggle, etc.)
Composite- Blend multiple inputsSwitch- Select between inputs
VideoPlayer- Video playback with codec support:- HAP (GPU-compressed, best performance)
- H.264, ProRes, MPEG-2
- Methods:
.play(),.pause(),.restart(),.seek(),.loop(),.speed()
Webcam- Camera capture (macOS: AVFoundation, Windows: Media Foundation)Image- Static image loading (PNG, JPG, BMP, TGA)
Particles- 2D particle system with physicsPointSprites- GPU point rendering
Timing & Sequencing:
Clock- BPM-based timing with swing and divisionsSequencer- 16-step pattern sequencer with trigger callbacksEuclidean- Euclidean rhythm generator
Drums:
Kick- 808-style kick with pitch envelopeSnare- Snare with tone/noise mixHiHat- Hi-hat with open/closed modesClap- Handclap with multiple bursts
Synthesis:
Oscillator- Waveforms (sine, saw, square, triangle)PolySynth- Polyphonic synthesizer with voice managementEnvelope- ADSR envelope generator
Effects:
Delay- Delay with feedbackReverb- Room reverbChorus,Flanger,Phaser- Modulation effectsCompressor,Limiter- DynamicsTapeEffect- Wow, flutter, saturation (vintage character)
Lo-fi:
Bitcrush- Bit/sample rate reductionOverdrive- Soft saturationCrackle- Vinyl crackle
Analysis:
FFT- Spectrum analysisBandSplit- Frequency band levels (bass/mid/high)BeatDetect- Beat/transient detectionLevels- RMS and peak metering
I/O:
AudioIn- Microphone/line inputAudioFile- Audio file playbackMidiIn- MIDI note/CC input
Primitives:
Box-.size(w, h, d),.flatShading()Sphere-.radius(),.segments(),.computeTangents()Cylinder-.radius(),.height(),.segments(),.flatShading()Cone-.radius(),.height(),.segments()Torus-.outerRadius(),.innerRadius(),.segments(),.rings()Plane-.size(w, h),.subdivisions()
CSG Boolean Operations:
Boolean-.inputA(),.inputB(),.operation(BooleanOp::Union/Subtract/Intersect)
Scene Composition:
SceneComposer- Compose multiple meshes with transforms and colorsRender3D- Render scenes with multiple shading modes
Shading Modes:
ShadingMode::PBR- Physically-based rendering (Cook-Torrance BRDF)ShadingMode::Flat- Per-fragment flat shadingShadingMode::Gouraud- Per-vertex shading (PS1-style)ShadingMode::Unlit- No lighting, pure color/texture
Lighting (supports up to 4 lights):
DirectionalLight- Sun-like parallel rays with direction, color, intensityPointLight- Omnidirectional light with position, color, intensity, rangeSpotLight- Cone-shaped light with position, direction, angle, falloffCameraOperator- Perspective camera with orbit controls
GPU Instancing:
InstancedRender3D- Render thousands of identical meshes in a single draw call- Per-instance transforms, colors, and material properties
- Use cases: asteroid fields, forests, crowds, particle debris
PBR Materials:
TexturedMaterial- Full PBR material with texture maps:.baseColor(),.normal(),.metallic(),.roughness(),.ao(),.emissive()
IBLEnvironment- Image-based lighting from HDR environment maps
#include <vivid/vivid.h>
#include <vivid/effects/effects.h>
#include <vivid/video/video.h>
using namespace vivid;
using namespace vivid::effects;
using namespace vivid::video;
void setup(Context& ctx) {
auto& chain = ctx.chain();
auto& video = chain.add<VideoPlayer>("video");
video.file = "assets/videos/my-video.mov";
video.loop(true);
auto& hsv = chain.add<HSV>("color");
hsv.input(&video);
hsv.saturation = 1.2f;
chain.output("color");
}
void update(Context& ctx) {
auto& video = ctx.chain().get<VideoPlayer>("video");
// Space to pause/play
if (ctx.key(GLFW_KEY_SPACE).pressed) {
video.isPlaying() ? video.pause() : video.play();
}
}
VIVID_CHAIN(setup, update)#include <vivid/vivid.h>
#include <vivid/render3d/render3d.h>
using namespace vivid;
using namespace vivid::render3d;
void setup(Context& ctx) {
auto& chain = ctx.chain();
// Create geometry
auto& box = chain.add<Box>("box");
box.size(1.0f, 1.0f, 1.0f);
auto& sphere = chain.add<Sphere>("sphere");
sphere.radius(0.6f);
sphere.segments(32);
// CSG: subtract sphere from box
auto& csg = chain.add<Boolean>("csg");
csg.inputA(&box);
csg.inputB(&sphere);
csg.operation(BooleanOp::Subtract);
// Scene composition
auto& scene = SceneComposer::create(chain, "scene");
scene.add(&csg, glm::mat4(1.0f), glm::vec4(0.9f, 0.3f, 0.3f, 1.0f));
// Camera and lighting
auto& camera = chain.add<CameraOperator>("camera");
camera.orbitCenter(0, 0, 0);
camera.distance(5.0f);
camera.fov(50.0f);
auto& sun = chain.add<DirectionalLight>("sun");
sun.direction(1, 2, 1);
sun.intensity = 1.5f;
// Render
auto& render = chain.add<Render3D>("render");
render.setInput(&scene);
render.setCameraInput(&camera);
render.setLightInput(&sun);
render.setShadingMode(ShadingMode::PBR);
render.metallic = 0.1f;
render.roughness = 0.5f;
chain.output("render");
}
void update(Context& ctx) {
// Animate camera orbit
auto& camera = ctx.chain().get<CameraOperator>("camera");
camera.azimuth(static_cast<float>(ctx.time()) * 0.3f);
}
VIVID_CHAIN(setup, update)#include <vivid/vivid.h>
#include <vivid/effects/effects.h>
#include <vivid/audio/audio.h>
#include <vivid/audio_output.h>
using namespace vivid;
using namespace vivid::effects;
using namespace vivid::audio;
void setup(Context& ctx) {
auto& chain = ctx.chain();
// Audio: drum machine
auto& clock = chain.add<Clock>("clock");
clock.bpm = 120.0f;
auto& kickSeq = chain.add<Sequencer>("kickSeq");
kickSeq.steps = 16;
kickSeq.setPattern(0b0001000100010001);
auto& kick = chain.add<Kick>("kick");
auto& bands = chain.add<BandSplit>("bands");
bands.input("kick");
auto& audioOut = chain.add<AudioOutput>("audioOut");
audioOut.setInput("kick");
chain.audioOutput("audioOut");
// Visuals: bass-reactive particles
auto& noise = chain.add<Noise>("noise");
noise.scale = 4.0f;
auto& flash = chain.add<Flash>("flash");
flash.input(&noise);
flash.decay = 0.9f;
flash.color.set(1.0f, 0.5f, 0.2f);
chain.output("flash");
// Connect audio triggers to visuals
auto* chainPtr = &chain;
kickSeq.onTrigger([chainPtr](float velocity) {
chainPtr->get<Kick>("kick").trigger();
chainPtr->get<Flash>("flash").trigger(velocity);
});
}
void update(Context& ctx) {
auto& chain = ctx.chain();
auto& clock = chain.get<Clock>("clock");
if (clock.triggered()) {
chain.get<Sequencer>("kickSeq").advance();
}
// Modulate visuals from audio analysis
float bass = chain.get<BandSplit>("bands").bass();
chain.get<Noise>("noise").scale = 4.0f + bass * 10.0f;
chain.process(ctx);
}
VIVID_CHAIN(setup, update)Vivid includes a built-in chain visualizer powered by ImGui and ImNodes.
Tab- Toggle the visualizer overlayF- Toggle fullscreenV- Toggle vsync (in examples that support it)Ctrl+Drag- Pan the chain visualizerEsc- Quit
- Node Graph - See your operator chain as connected nodes
- Live Thumbnails - Each node shows its real-time output texture
- Parameter Display - View current parameter values on each node
- Connection Visualization - See how operators are wired together
- Performance Overlay - FPS, frame time, and resolution display
vivid/
├── core/ # Runtime engine with integrated UI
│ ├── src/ # Main runtime, hot-reload, addon discovery
│ ├── include/vivid/ # Public API headers
│ ├── imgui/ # Chain visualizer (ImGui/ImNodes)
│ └── shaders/ # Blit and text shaders
├── addons/ # Optional feature packages
│ ├── vivid-io/ # Image loading utilities (shared by other addons)
│ ├── vivid-effects-2d/ # 2D texture operators (always linked)
│ ├── vivid-video/ # Video playback (HAP, H.264, etc.)
│ └── vivid-render3d/ # 3D rendering (PBR, CSG, IBL)
├── examples/ # Curated user examples (by category)
├── testing-fixtures/ # Test examples for CI/regression
└── assets/ # Shared resources
Examples are organized by category. See examples/README.md for the full learning path.
| Category | Example | Description |
|---|---|---|
| Getting Started | 01-template |
Heavily commented starter |
| Getting Started | 02-hello-noise |
Minimal noise generator |
| 2D Effects | chain-basics |
Multi-operator chain with image distortion |
| 2D Effects | feedback |
Recursive feedback effects |
| 2D Effects | particles |
2D particle system with physics |
| 2D Effects | retro-crt |
Full retro post-processing pipeline |
| Audio | drum-machine |
Drum synthesis and sequencing |
| Audio | audio-reactive |
Audio analysis driving visuals |
| 3D Rendering | 3d-basics |
Primitives, camera, CSG, lighting |
| 3D Rendering | gltf-loader |
GLTF/GLB model loading |
| 3D Rendering | instancing |
GPU instanced rendering |
Run any example:
./build/bin/vivid examples/getting-started/01-templateAddons are automatically discovered by scanning your chain.cpp #include directives:
#include <vivid/effects/noise.h> // → vivid-effects-2d addon
#include <vivid/video/player.h> // → vivid-video addonEach addon has an addon.json with metadata:
{
"name": "vivid-video",
"version": "0.1.0",
"operators": ["VideoPlayer", "AudioPlayer"]
}The hot-reload system automatically adds include paths and links libraries for discovered addons.
Vivid is designed with AI-assisted development in mind:
- Minimal Core - ~600 lines of runtime code that fits in context windows
- Self-Contained Operators - Each operator is a single .h/.cpp pair with embedded shaders
- Consistent Patterns - All operators follow the same structure (init/process/cleanup)
- Comprehensive Documentation - LLM-optimized reference docs and recipes
- Hot Reload - Instant feedback loop when iterating with AI assistance
- Automatic State Management - No boilerplate for chain lifecycle
| File | Purpose |
|---|---|
| docs/LLM-REFERENCE.md | Compact operator reference (~200 lines) |
| docs/RECIPES.md | Complete chain.cpp examples for common effects |
| ROADMAP.md | Full architecture and development history |
Create a CLAUDE.md file in your project folder to give AI assistants context about your specific project:
# My Vivid Project
## Goal
[What effect you're trying to create]
## Current Chain
[Brief description of your operator chain]
## Resources
- docs/LLM-REFERENCE.md - Operator reference
- docs/RECIPES.md - Effect examplesSee examples/getting-started/01-template/ for a complete starter project with CLAUDE.md.
MIT
Contributions welcome! Please read the ROADMAP.md for current development priorities.







