FreeVJ FULL AUTO VJ LOOP

FreeVJ / Loop library / Sequence

Ai Flipping Hex Wave

Precise white honeycomb grid rippling with flipping hexagons on black.

Ai Flipping Hex Wave
Play this loop live

Exports are made in this browser and saved directly.

GLSL source

const float SEQ_N = 32.0;
const float SEQ_COLS = 5.0;
const float SEQ_ROWS = 7.0;
const float SEQ_LUMA_ALPHA = 1.0;
const float SEQ_COVER = 0.0;
// Sequence (seq@1) shared shader — plays the frames of a greyscale atlas (uTex) by mode.
// SEQ_N · SEQ_COLS · SEQ_ROWS · SEQ_LUMA_ALPHA · SEQ_COVER constants are prepended.
// uParams: x=mode (0 A→B · 1 B→A · 2 A→B→A · 3 random N s each · 4 sliced panes · 5 slot reel) y=speed (frames/beat) z=hold (s) w=panes (panes · reels)
// Neighbouring frames are crossfaded so even few frames flow — a morph melts A into B this way.

float seqCell(float f, vec2 p) {
    if (p.x < 0.0 || p.y < 0.0 || p.x > 1.0 || p.y > 1.0) return 0.0;
    f = mod(floor(f), SEQ_N);
    vec2 grid = vec2(SEQ_COLS, SEQ_ROWS);
    vec2 cell = vec2(mod(f, SEQ_COLS), floor(f / SEQ_COLS));
    vec2 inset = 0.75 / vec2(textureSize(uTex, 0)) * grid;      // keep samples off the neighbouring cells
    vec2 q = clamp(vec2(p.x, 1.0 - p.y), inset, 1.0 - inset);
    return texture(uTex, (cell + q) / grid).r;
}

float seqAt(float pos, vec2 p) {
    return mix(seqCell(pos, p), seqCell(pos + 1.0, p), smoothstep(0.0, 1.0, fract(pos)));
}

// window (aspect win) coords → frame coords. contain (masks · symbols: never cropped) · cover (video: fill)
vec2 seqFit(vec2 q, float win) {
    vec2 ts = vec2(textureSize(uTex, 0));
    float cell = (ts.x / SEQ_COLS) / max(1.0, ts.y / SEQ_ROWS);
    vec2 p = q - 0.5;
    bool wide = win > cell;
    if ((SEQ_COVER > 0.5) != wide) p.x *= win / cell;
    else p.y *= cell / win;
    return p + 0.5;
}

vec2 prim(vec2 uv) {
    float mode = floor(uParams.x + 0.5);
    float sp = max(0.05, uParams.y);
    float aspect = uResolution.x / max(1.0, uResolution.y);
    float n = SEQ_N;
    float l = 0.0;

    if (mode < 3.5) {
        float pos;
        if (mode < 0.5) pos = uBeat * sp;                                   // A → B
        else if (mode < 1.5) pos = -uBeat * sp;                             // B → A
        else if (mode < 2.5) {                                              // A → B → A
            float per = max(1.0, 2.0 * (n - 1.0));
            float x = mod(uBeat * sp, per);
            pos = x < n - 1.0 ? x : per - x;
        } else {                                                            // random N s each — every segment flows from a different start frame
            float hold = max(0.25, uParams.z);
            float k = floor(uTime / hold);
            float t = uTime - k * hold;
            pos = floor(h11(k * 7.13 + uSeed * 0.37) * n) + t * sp * 2.0;
            l = smoothstep(0.0, 0.06, t);                                   // dip very briefly at the cut (no flash)
        }
        float v = seqAt(pos, seqFit(uv, aspect));
        l = mode > 2.5 ? v * l : v;
    } else if (mode < 4.5) {                                                // panes — sliced into vertical panes with their own phase, flowing up and down alternately
        float k = clamp(floor(uParams.w + 0.5), 1.0, 8.0);
        float i = floor(uv.x * k);
        float lx = fract(uv.x * k);
        float dir = mod(i, 2.0) < 0.5 ? 1.0 : -1.0;
        vec2 q = vec2(uv.x, fract(uv.y + dir * uBeat * 0.125 * sp));
        l = seqAt(uBeat * sp + i * n / k, seqFit(q, aspect));
        l *= smoothstep(0.0, 0.02, lx) * smoothstep(1.0, 0.98, lx);        // gap between panes
    } else {                                                                // slot reel — reels spin, then stop one by one with a small bounce
        float k = clamp(floor(uParams.w + 0.5), 1.0, 8.0);
        float hold = max(0.5, uParams.z);
        float i = floor(uv.x * k);
        vec2 q = vec2(fract(uv.x * k), uv.y);
        float cyc = floor(uTime / hold);
        float t = fract(uTime / hold);
        float stopAt = min(0.92, 0.3 + i * 0.6 / k);
        float from = floor(h11((cyc - 1.0) * 3.1 + i * 17.7 + uSeed) * n);
        float to = floor(h11(cyc * 3.1 + i * 17.7 + uSeed) * n);
        float e = 1.0 - pow(1.0 - clamp(t / stopAt, 0.0, 1.0), 3.0);
        float bounce = sin(clamp((t - stopAt) / 0.1, 0.0, 1.0) * 3.14159) * 0.06;
        float s = from + e * (2.0 * n + mod(to - from + n, n)) - bounce;
        float y = q.y + s;
        float win = aspect / k;
        l = seqCell(floor(y), seqFit(vec2(q.x, fract(y)), win));
        float spin = 1.0 - smoothstep(0.55, 1.0, clamp(t / stopAt, 0.0, 1.0));
        l = mix(l, seqCell(floor(y - 0.18), seqFit(vec2(q.x, fract(y - 0.18)), win)), 0.45 * spin);   // blur while spinning
        l *= smoothstep(0.0, 0.025, q.x) * smoothstep(1.0, 0.975, q.x);
    }

    l = clamp(l, 0.0, 1.0);
    return vec2(l, SEQ_LUMA_ALPHA > 0.5 ? l : 1.0);
}

One vec2 prim(vec2 uv) function using the shared uniform contract (uBeat · uBass · uKick …). It outputs luma and alpha only.

Similar loops

☕ Buy me a coffee

FreeVJ is free. If you enjoy it, support the maker with a coffee's worth of USDT.

USDT · TRON (TRC20) TRC20 network only

TVg11kWumyqNpRU9HXKo8a8u2fa8sdJtKh

☕ 3 USDT☕☕ 5 USDT☕☕☕ 10 USDT

How to send with Binance
  1. Binance app: Wallet › Withdraw › USDT › paste the address (or scan the QR) › network TRC20 › amount › confirm. The address belongs to Binance, so it may go through as a free internal transfer.
  2. Other wallets and exchanges (Trust Wallet, OKX, Bybit …): send USDT on the TRC20 network to the same address. A self-custody wallet needs a little TRX for the fee.

Always send on the TRC20 network. USDT sent on another network is hard to recover.