VKO1 Android · join android closed beta
VKO1 join android closed beta
VisualKO1 · Android

VJ in your
pocket.

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.

EngineOpenGL ES 3.1
Min SDK31 · Android 12
Pads16 × 8 banks
Latency< 20 ms
SHADER · PARTICLES
60FPS · 1080P
See it in action

Watch the demo.

01
Native UI · Material You

Designed for thumbs.

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.

9:41
📶🔋
VKO1
📡 📁 ?
BPM
94
PLASMA · A06
CASTING
Material You Theme follows your wallpaper. VKO1 reads Android 12+ dynamic colors and tints chrome accordingly. Pads, accents, and indicator orange are sourced from colorPrimary/colorTertiary when available — otherwise fall back to the canonical EP-133 orange.
Cast & External Display One feed, two screens. Use Wi-Fi cast (Google Cast / Miracast) or plug a DP-Alt USB-C cable into a TV. The phone shows the controller; the external screen renders the visuals at full resolution. Per-display FX overrides supported.
GPU · OpenGL ES 3.0 / Vulkan Built for Android's GPU stack. The Android port runs custom shaders as GLSL through OpenGL ES 3.0 (Vulkan on capable devices) — separate from the iOS Metal pipeline. Same U.* uniforms and knob mappings, but the shader dialect is GLSL, not MSL. Porting between platforms takes minutes, not minutes-times-zero.
USB MIDI · USB Audio · Bluetooth LE MIDI Plug in any controller. Class-compliant MIDI works out of the box — Launchpads, KO II, OP-Z, MPC-style grid pads. Map any pad/CC to any clip or knob in the in-app learn mode.
02
Custom shaders · Live editor

Write a shader.
Hit play.

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.


        
p0 · Speed1.00
p1 · Reactivity0.80
p2 · Complexity0.50
p3 · Jog0.00
01
Three modes.
Snippet (2D) — default. Write 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).
02
Use the U block.
Every live signal lives on U.*U.time, U.beat, U.bass, U.energy, plus four user knobs U.p0U.p3.
03
Respect the knobs.
Multiply time by 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.
04
Android safety.
Guard against the Android 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);
U. Block · Uniforms reference
U.timefloat
Seconds since pad started.
U.beatfloat
0..1 — fires on detected beat.
U.beatPhasefloat
0..1 sawtooth inside the current beat.
U.beatStrengthfloat
0..1 confidence of beat detection.
U.energyfloat
0..1 overall audio level.
U.bassfloat
0..1 low-frequency level.
U.midfloat
0..1 mid-frequency level.
U.treblefloat
0..1 high-frequency level.
U.resolutionvec2
Viewport size in pixels.
U.paletteint
0..32 active palette index.
U.p0 · Speedfloat
Time multiplier. 0=frozen, 1=normal.
U.p1 · Reactfloat
Audio influence. 0=none, 1=full.
U.p2 · Complexfloat
Detail / density knob.
U.p3 · Jogfloat
Jogwheel scrub rate, −3..3 (0 idle).
3D mode · vertex + fragment

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.

Pick a mesh.
MeshBest for
Torus KnotWrapping patterns, knot shaders
IcosphereGlobe-style shading, SDF displacement
CubeBox projections, cubemap effects
Plane ✨Screen-space / immersive raymarching — UV spans 0..1 flat; each fragment is a camera ray
SphereSmooth sphere normals
ImportedYour custom OBJ (UVs + normals required)
Two functions, one file.
Fragment is required — the engine detects 3D mode from it. Vertex is optional; omit it and the engine supplies a default pass-through. Both live in the same source file.
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);
}
The scene block.
In addition to all 2D 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
Palette helper: get_palette_color_3d(t, U.palette) — same 33 palettes as 2D mode. get_vko_palette_color also works.
Plane mesh → immersive raymarching.
Set Mesh → Plane and emit a fullscreen NDC quad in 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);
}
Recipes · copy & remix
Beat-Sync FlashU.beat · U.p1
// sharp flash on beat, decays quickly
float flash = U.beat * U.p1 * 0.8 * exp(-U.beatPhase * 4.0);
col += vec3(flash);
Bass-Driven DistortionU.bass · U.p1
float kick   = U.bass * U.p1;
vec2  warped = p + p * kick * 0.15;
float d      = length(warped);
Aspect-Correct CoordsAndroid-safe
// 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);
Jogwheel Slit-ScanU.p3
float scanLine = uv.y + U.time * 0.05
                 + U.p3 * 0.3;
vec3 col       = get_vko_palette_color(
                       fract(scanLine), U.palette);