The full VKO1 sample-and-trigger engine, rebuilt for Android. Sixteen pads, eight banks, beat-locked auto modes, audio-reactive shaders. Cast to TV, projector, or external display over Wi-Fi or USB-C DP-Alt.
Same workflow as the iOS and Mac apps, retuned for one-handed phone use. Larger pad targets, edge-friendly transport, and a swipe-up bank chooser so you can fire the next pad without looking.
colorPrimary/colorTertiary when available — otherwise fall back to the canonical EP-133 orange.
U.* uniforms and knob mappings, but the shader dialect is GLSL, not MSL. Porting between platforms takes minutes, not minutes-times-zero.
VKO1 ships a runtime GLSL compiler. Tap any empty pad → NEW SHADER → start typing. The engine recompiles on every save and routes your fragment through the same FX chain as built-in clips. No build, no upload, no app reload.
vec4 vko_main(vec2 uv). 3D — write vko_fragment (and optionally vko_vertex) for mesh shading or Plane raymarching. Full Shader — starts with #version 450, you own everything (REMIX / Shadertoy ports).U.* — U.time, U.beat, U.bass, U.energy, plus four user knobs U.p0–U.p3.U.p0. Scale audio influence by U.p1. Drive detail with U.p2. Add U.p3 * 0.4 as a phase offset for the jogwheel scrub.0×0 layout flicker on rotate/resize — it can divide-by-zero into a NaN black screen. The canonical pattern: float aspect = U.resolution.x / max(U.resolution.y, 1.0);When your source defines vko_fragment, the engine compiles it as a 3D shader. You get a mesh, world-space normals, and a scene uniform block with camera matrices. Pick a mesh in the inspector.
| Mesh | Best for |
|---|---|
| Torus Knot | Wrapping patterns, knot shaders |
| Icosphere | Globe-style shading, SDF displacement |
| Cube | Box projections, cubemap effects |
| Plane ✨ | Screen-space / immersive raymarching — UV spans 0..1 flat; each fragment is a camera ray |
| Sphere | Smooth sphere normals |
| Imported | Your custom OBJ (UVs + normals required) |
void vko_vertex(in Vertex3DIn v, inout Vertex3DOut o) { // audio-reactive vertex displacement float lift = sin(v.uv.x * 12.0 + U.time) * 0.1 * scene.beatStrength; o.worldPos += v.normal * lift; o.position = scene.projectionMatrix * scene.viewMatrix * vec4(o.worldPos, 1.0); o.displacement = lift; } vec4 vko_fragment(Vertex3DOut v) { vec3 N = normalize(v.worldNormal); float lit = dot(N, vec3(0.0, 1.0, 0.0)) * 0.5 + 0.5; vec3 col = get_palette_color_3d(lit + U.time * 0.05, U.palette); return vec4(col, 1.0); }
U.* uniforms, 3D shaders get a scene block exposing camera + transform state:scene.modelMatrix // mat4 — mesh model→world scene.viewMatrix // mat4 — camera view scene.projectionMatrix // mat4 — perspective scene.cameraPosition // vec3 — world-space camera scene.beatStrength // float — same as U.beatStrength
get_palette_color_3d(t, U.palette) — same 33 palettes as 2D mode. get_vko_palette_color also works.vko_vertex to bypass the scene camera. Each fragment becomes one screen pixel and one camera ray — perfect for SDFs and volumetrics. Only works on Plane; other meshes have curved UVs.// FULLSCREEN NDC QUAD — bypasses scene camera void vko_vertex(in Vertex3DIn v, inout Vertex3DOut o) { o.position = vec4(v.uv * 2.0 - 1.0, 0.0, 1.0); o.uv = v.uv; } vec4 vko_fragment(Vertex3DOut v) { float aspect = U.resolution.x / max(U.resolution.y, 1.0); vec2 coord = (v.uv * 2.0 - 1.0) * vec2(aspect, 1.0); vec3 ro = vec3(0.0, 0.0, 3.0); // ray origin vec3 rd = normalize(vec3(coord, -1.5)); // ray direction // … raymarch your SDF here … return vec4(rd * 0.5 + 0.5, 1.0); }
// sharp flash on beat, decays quickly
float flash = U.beat * U.p1 * 0.8 * exp(-U.beatPhase * 4.0);
col += vec3(flash);
float kick = U.bass * U.p1;
vec2 warped = p + p * kick * 0.15;
float d = length(warped);
// guards Android 0x0 layout flicker
float aspect = U.resolution.x / max(U.resolution.y, 1.0);
vec2 p = (uv * 2.0 - 1.0) * vec2(aspect, 1.0);
float scanLine = uv.y + U.time * 0.05
+ U.p3 * 0.3;
vec3 col = get_vko_palette_color(
fract(scanLine), U.palette);