# VKO1 Graph Editor — context for AI assistants You are helping a user build audio-reactive visuals in the VKO1 Graph Editor, a browser app (WebGL2). The user patches a node graph and writes GLSL shaders that render live and export unchanged into VKO1 for Android / iOS / After Effects. This file is the contract — follow the exact names and signatures below. ## How the app works - Open the node library with the + button (or `N`). Tabs: Templates, Generators, Modifiers, Combiners, Inputs, Output. - A graph needs exactly one **Output** node; the final image lands there. - Drag from an orange **Output port** to an input port to wire nodes. Orange ports carry textures (image data); blue ports are **float inputs** (0..1) for audio-reactive control. - Tap a node to edit its parameters in the left inspector. Right-click for duplicate / mute / edit code. - Live audio drives everything: SIM (BPM clock), MIC (microphone), or FILE (audio file / folder playlist). MIDI input nodes work over WebMIDI. ## Custom shader API — 2D (GeneratorType `CustomShader`) Implement exactly: vec4 vko_main(vec2 uv) // uv in 0..1, return RGBA 0..1 Available on the `U` uniform block: - `U.time` seconds, monotonic - `U.beat` 0..1 beat pulse - `U.beatPhase` 0..1 phase within the beat - `U.beatStrength`0..1 onset strength - `U.energy` 0..1 overall loudness - `U.bass` `U.mid` `U.treble` 0..1 per-band - `U.p0` speed (generator "speed" param / wired pin) - `U.p1` reactivity - `U.p2` complexity - `U.p3` jog / scale trim - `U.palette` int 0..32, current palette index - `U.resolution` vec2 pixel size Palette helper (33 built-in palettes): `get_vko_palette_color(float t, int i)` → vec3. Pass `U.palette` as `i`. Example: vec4 vko_main(vec2 uv) { float t = U.time * (0.5 + U.p0); vec2 p = uv - 0.5; float d = length(p) * (4.0 + U.p2 * 8.0); float v = sin(d - t * 3.0) * 0.5 + 0.5; v += U.bass * U.p1 * 0.6; // bass-reactive return vec4(get_vko_palette_color(v, U.palette), 1.0); } ## Custom shader API — 3D (GeneratorType `CustomShader3D`) Implement `vko_fragment` and optionally `vko_vertex`; the shader renders on a real mesh (torus knot, icosphere, cube, plane, sphere — set by `mesh3DKind`). Same `U` uniforms apply. 3D previews on-device (Filament on Android); the web shows a 2D fallback. ## Node reference Generators: Plasma, Voronoi, Fractal, Tunnel, Starfield, Noise, Kaleidoscope, Hypnotic, Ripple, Vortex, ColorField, Gradient, EQBars, Spectrum, Oscilloscope, Waveform, Particles, Pulsar, LevelMeter, SpectrumRings, ShardBurst, Grid, GridPulse, Rings, Lissajous, Matrix, GlyphMatrix, NeonPrism, Mandelbrot, Julia, Reaction, Conway, LaserWeb, SignalDecode, TacticalGrid, SchematicPulse, TerminalLoad, TronGrid, WireTunnel, VectorTerrain, HoloCube, TorusKnot3D, Icosphere3D, Model3D, Amazing3D, MediaMesh3D, CustomShader, CustomShader3D. Generator float pins (blue, wire Input nodes here): `reactivity`, `complexity`, `speed`, `palette` (+ 3D: `rotationX/Y/Z`, `scale3D`). Modifiers (image → image): Blur, Mirror, Kaleidoscope, ColorShift, HueRotate, EdgeDetect, Glitch, Feedback, Datamosh, ChromaKey, ColorRamp, Pixelate, Saturation, Scanlines, Bloom, WaveWarp, RGBSplit, Twirl, Vortex, Pinch, Perspective3D, AffineTransform, FilmLook, VariableBlur, Posterize, Solarize, ThermalMap, NoiseWarp, Sharpen, Rotate. Combiners (two textures → one): Blend, Mask, Difference, Displace, BeatCrossfade, LumaKey. Blend exposes 24 blend modes. Inputs (float sources, wire into blue pins): Time, BeatPhase, BeatStrength, Energy, Spectrum, Bass, Mid, Treble, LFO, MIDI_CC, MIDI_Note, JogWheel, Constant, Animation3D, Microphone, Palette. ### Palette node (cycles color palettes) Wire its output to a generator's `palette` pin. Param `paletteMode`: 0 = Beat Advance (steps +1 palette on each beat), 1 = Random on Beat, 2 = LFO Cycle (`rate` palettes/sec), 3 = Static (fixed `paletteIndex`). `beatThreshold` sets the beat trigger sensitivity. Palettes (index 0..32): Cyberpunk, Neon, Fire, Ice, Nature, Synthwave, Mono, Rainbow, Toxic, Blood Moon, Electric, Lava, UV, Candy, Matrix, Sunset, Ocean, Plasma, Rust, Glacier, Aurora, Thermal, Mono Red, Mono Blue, Mono Green, Mono Amber, Mono Cyan, Mono Pink, Deep Space, Gold, Retro, Vapor, Infrared. ## Export EXPORT (top-right) → `.vko` (Android bundle: graph + custom shader sources), Graph JSON, Android (Vulkan GLSL), AUv3 Player `.vkograph`, Web / iOS (Metal) / After Effects. IMPORT reads `.vko` / `.json`. ## Recipes for great-looking shaders Reusable GLSL building blocks. Paste the helpers above `vko_main` and combine. All are self-contained (no engine functions beyond the `U` block + palette helper). Hash + value noise + fbm (the backbone of organic motion): float hash(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } float noise(vec2 p){ vec2 i = floor(p), f = fract(p); vec2 u = f*f*(3.0-2.0*f); return mix(mix(hash(i), hash(i+vec2(1,0)), u.x), mix(hash(i+vec2(0,1)), hash(i+vec2(1,1)), u.x), u.y); } float fbm(vec2 p){ float v = 0.0, a = 0.5; for(int i=0;i<5;i++){ v += a*noise(p); p *= 2.0; a *= 0.5; } return v; } Key techniques: - **Center + aspect-correct:** `vec2 p = (uv - 0.5); p.x *= U.resolution.x / U.resolution.y;` - **Domain warping** (liquid, flame, marble): feed noise into itself — `float w = fbm(p*3.0 + fbm(p*2.0 + U.time*0.2));` - **Polar / kaleidoscope:** `float a = atan(p.y,p.x), r = length(p); a = mod(a, 6.2832/6.0);` then rebuild `p = r*vec2(cos(a),sin(a));` for n-fold symmetry. - **SDF glow:** `float d = abs(length(p)-0.3); col += 0.02/max(d,1e-3);` for neon rings. - **Beat pump:** scale or brightness by `(1.0 + U.beat * U.p1)`; punch onsets with `U.beatStrength`. - **Audio bands:** bass → size/scale, mid → motion, treble → sparkle/edges. - **Color:** drive `get_vko_palette_color(t, U.palette)` with a noise/distance field for `t`; respect the user's palette instead of hardcoding RGB. - **Polish:** vignette `col *= 1.0 - 0.4*dot(p,p);` and subtle RGB offset for depth. Example A — bass-reactive liquid plasma (domain-warped fbm + palette): vec4 vko_main(vec2 uv){ vec2 p = (uv-0.5); p.x *= U.resolution.x/U.resolution.y; float t = U.time*(0.2 + 0.4*U.p0); float w = fbm(p*3.0 + vec2(fbm(p*2.0+t), fbm(p*2.0-t))); w += U.bass * U.p1 * 0.5; // bass swells the field vec3 col = get_vko_palette_color(w + t*0.1, U.palette); col *= 1.0 - 0.5*dot(p,p); // vignette return vec4(col, 1.0); } Example B — neon kaleidoscope rings that pulse on the beat: vec4 vko_main(vec2 uv){ vec2 p = (uv-0.5); p.x *= U.resolution.x/U.resolution.y; float a = atan(p.y,p.x), r = length(p); a = mod(a, 6.2832/8.0) - 6.2832/16.0; // 8-fold symmetry vec2 q = r*vec2(cos(a),sin(a)); float ring = abs(sin(r*18.0 - U.time*3.0)); float glow = 0.015/max(ring,1e-3); glow *= 1.0 + U.beat*U.p1*2.0; // beat pump vec3 col = get_vko_palette_color(r + U.treble*0.3, U.palette) * glow; return vec4(col, 1.0); } When the user says "make it cooler": add a layer (warp the UV first), raise contrast (`pow(col, vec3(1.3))`), tie one more parameter to audio, or cycle the palette with a Palette node. Keep it readable and keep the `U.` contract. ## How to help - When asked to write or fix a shader: edit the code in the SHADER panel (textarea), keep the `vko_main` / `vko_fragment` signature, use only the `U` uniforms above, then click ▶ COMPILE. Compile errors show inline — read them and iterate. - For motion that reacts to music, prefer `U.bass` / `U.beat` / `U.energy` times `U.p1` (reactivity). - Don't invent uniform names. If a value isn't in the `U` list, it doesn't exist. - Always review edits with the user before they save.