Skip to main content

ns-ui / loud

Magnetron Racetrack Sweep

A full-bleed background built on sputter target erosion: a magnetron's plasma racetrack sweeps back and forth across a target face, slowly grooving it wherever the ring has lingered, rendered as a persistent radial erosion field under a live, non-accumulated glow ring.

Use when Pick magnetron-racetrack-sweep for a full-bleed background whose motion is a slow, physically justified sweep leaving a legible structural trace behind it — the erosion groove gives the field a persistent "memory" a generic looping gradient never accumulates. Pick edm-crater-field instead when the surface should read as a statistical steady-state (constant birth/decay, never accumulating toward one shape) rather than a slowly deepening groove; weld-pool when the background needs to carry rasterized headline type inside the same height field.

Install

npx shadcn add https://design.helpmarq.com/r/magnetron-racetrack-sweep.json

Ask AI

Point an assistant at this component's docs (llms-full.txt) with one click.

Claude, ChatGPT, Grok, and Perplexity open with the prompt already in. Gemini copies it to your clipboard first. Paste it in once the chat opens.

Source
registry/loud/magnetron-racetrack-sweep/component.tsx
"use client";

import { useEffect, useId, useRef } from "react";

// ---------------------------------------------------------------------------
// MagnetronRacetrackSweep — a full-bleed background eroded by sputter target
// wear: a magnetron's magnetic field confines plasma into an annular
// "racetrack" band on a planar sputter target's face. Production cathodes
// sweep that racetrack's radius back and forth (rotating/oscillating magnet
// assembly) so erosion doesn't dig one fixed groove and waste the rest of the
// target.
//
// EROSION IS RADIALLY SYMMETRIC, SO THE ACCUMULATION BUFFER IS 1D. The
// racetrack is a full annulus at every instant, never a spot — the whole
// erosion history is therefore a function of radius alone, never angle. That
// lets the "persistent, never-cleared accumulation buffer" (same non-clearing
// technique as rime-creep and edm-crater-field, monotonic here rather than
// decaying — real erosion never heals) live in a 256-texel 1D LUMINANCE
// texture instead of a full 2D grid: cheap to stamp, cheap to sample, and it
// is the mechanic's own physical symmetry that licenses the simplification,
// not a performance shortcut taken against the spec.
//
// The composite pass derives a surface normal from the 1D depth field's
// RADIAL gradient only (there is no tangential slope, by construction) and
// projects it back into 2D along the local radial direction — the target
// reads as a real machined groove, brighter/darker as it catches the
// achromatic studio's light bands, not a flat painted ring.
//
// The live glow ring is a SEPARATE, non-accumulated quantity: it is redrawn
// every frame from the sweep's current phase and added to luminance only
// (never colour — real sputtering plasma glows violet/pink, discarded on
// purpose per the monochrome constraint). A pointer brightens the ring
// locally by angle, never by hue, and never touches the sweep clock itself.
// ---------------------------------------------------------------------------

export interface MagnetronRacetrackSweepProps {
  /** Freezes the surface on a composed steady-state frame without unmounting. */
  paused?: boolean;
  /** Rendered in the DOM over the field — eyebrow, headline, CTA. */
  children?: React.ReactNode;
  className?: string;
  style?: React.CSSProperties;
}

const VERT_SRC = `
attribute vec2 a_pos;
void main() { gl_Position = vec4(a_pos, 0.0, 1.0); }
`;

const FRAG_SRC = `
precision highp float;

uniform vec2 u_size;       // css px
uniform float u_dpr;
uniform vec2 u_center;     // css px, target centre
uniform float u_targetR;   // css px, target radius
uniform sampler2D u_erosion; // 1D depth field, N x 1
uniform float u_erosionN;
uniform float u_ringR;         // current live sweep radius, normalized 0..1
uniform float u_ringHalfWidth; // normalized
uniform float u_pointerAngle;
uniform float u_pointerActive;
uniform float u_time;
uniform vec3 u_c0;
uniform vec3 u_c1;
uniform vec3 u_c2;
uniform vec3 u_c3;
uniform vec3 u_c4;
uniform vec3 u_bg;
uniform float u_bias;
uniform float u_contrast;

float sampleDepth(float r) {
  return texture2D(u_erosion, vec2(clamp(r, 0.0, 1.0), 0.5)).r;
}

float strip(float el, float at, float width) {
  float d = (el - at) / width;
  return exp(-d * d);
}

// simplified achromatic studio — a machined-metal read needs fewer bands
// than a mirror pool, same family as edm-crater-field's env().
float env(vec3 r) {
  float el = r.y;
  float az = atan(r.x, r.z);
  float L = 0.42;
  L += 0.20 * smoothstep(0.05, 0.62, el);
  L -= 0.18 * smoothstep(0.0, -0.55, el);
  L += 0.28 * strip(el, 0.14, 0.06);
  L += 0.14 * strip(el, 0.46, 0.09);
  L += 0.08 * exp(-pow(sin(az * 1.0 + u_time * 0.03) / 0.42, 2.0));
  return L;
}

vec3 ramp(float x) {
  vec3 c = mix(u_c0, u_c1, smoothstep(0.0, 0.30, x));
  c = mix(c, u_c2, smoothstep(0.28, 0.58, x));
  c = mix(c, u_c3, smoothstep(0.55, 0.85, x));
  c = mix(c, u_c4, smoothstep(0.82, 1.0, x));
  return c;
}

void main() {
  vec2 p = vec2(gl_FragCoord.x, u_size.y * u_dpr - gl_FragCoord.y) / u_dpr - u_center;
  float dist = length(p);
  float r = dist / u_targetR;

  float edge = smoothstep(1.02, 0.985, r);
  if (edge <= 0.0) {
    gl_FragColor = vec4(u_bg, 1.0);
    return;
  }

  vec2 rDir = dist > 0.0005 ? p / dist : vec2(0.0, 1.0);
  float texel = 1.0 / u_erosionN;
  float d0 = sampleDepth(r);
  float dPlus = sampleDepth(r + texel);
  float dMinus = sampleDepth(r - texel);
  float slope = (dPlus - dMinus) / (2.0 * texel);
  float relief = 2.2;
  vec3 n = normalize(vec3(-slope * relief * rDir, 1.0));
  vec2 vp = p / u_targetR;
  vec3 v = normalize(vec3(vp.x * 0.5, -vp.y * 0.5, 1.0));
  vec3 refl = reflect(-v, n);

  float L = env(refl);
  vec3 l1 = normalize(vec3(-0.28, 0.7, 0.6));
  float s1 = pow(max(dot(refl, l1), 0.0), 90.0);
  L += s1 * 0.6;

  // material removed reads as LOWER relative luminance than the fresh face,
  // in both themes — direction never flips, only bias/contrast do.
  float depthVis = clamp(d0 / 1.4, 0.0, 1.0);
  L -= depthVis * 0.34;

  // live glow ring — additive luminance only, never accumulated, never hue.
  float ringMask = 1.0 - smoothstep(u_ringHalfWidth * 0.7, u_ringHalfWidth * 1.15, abs(r - u_ringR));
  L += ringMask * 0.5;

  if (u_pointerActive > 0.5) {
    float ang = atan(p.x, -p.y);
    float diff = ang - u_pointerAngle;
    diff = mod(diff + 3.14159265, 6.2831853) - 3.14159265;
    float angularBoost = exp(-pow(diff / 0.35, 2.0));
    L += ringMask * angularBoost * 0.35;
  }

  float fres = pow(1.0 - max(dot(n, v), 0.0), 5.0);
  L += fres * 0.08;

  float Lc = clamp((L - 0.5) * u_contrast + 0.5 + u_bias, 0.0, 1.0);
  vec3 col = mix(u_bg, ramp(Lc), edge);
  gl_FragColor = vec4(col, 1.0);
}
`;

type RGB = [number, number, number];

function parseHex(raw: string): RGB | null {
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(raw.trim());
  if (!m) return null;
  let h = m[1];
  if (h.length === 3) h = h.split("").map((c) => c + c).join("");
  const n = parseInt(h, 16);
  return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];
}

function mixRGB(a: RGB, b: RGB, t: number): RGB {
  return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
}

function luminance([r, g, b]: RGB): number {
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

function compile(gl: WebGLRenderingContext, type: number, src: string): WebGLShader {
  const s = gl.createShader(type)!;
  gl.shaderSource(s, src);
  gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
    const info = gl.getShaderInfoLog(s);
    gl.deleteShader(s);
    throw new Error(`magnetron-racetrack-sweep: shader compile failed: ${info ?? ""}`);
  }
  return s;
}

// Minimal full-bleed fragment-shader host, same shape as weld-pool /
// edm-crater-field's GLSurface: one program, one fullscreen triangle pair,
// lazily-resolved uniform locations.
class GLSurface {
  gl: WebGLRenderingContext | null = null;
  private program: WebGLProgram | null = null;
  private vs: WebGLShader | null = null;
  private fs: WebGLShader | null = null;
  private buffer: WebGLBuffer | null = null;
  private locs = new Map<string, WebGLUniformLocation | null>();

  constructor(private canvas: HTMLCanvasElement, private frag: string) {}

  init(): boolean {
    const gl = this.canvas.getContext("webgl", {
      alpha: false,
      antialias: false,
      premultipliedAlpha: false,
      powerPreference: "high-performance",
    }) as WebGLRenderingContext | null;
    if (!gl) return false;
    this.gl = gl;
    try {
      this.vs = compile(gl, gl.VERTEX_SHADER, VERT_SRC);
      this.fs = compile(gl, gl.FRAGMENT_SHADER, this.frag);
      const program = gl.createProgram();
      if (!program) {
        this.destroy();
        return false;
      }
      this.program = program;
      gl.attachShader(program, this.vs);
      gl.attachShader(program, this.fs);
      gl.linkProgram(program);
      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        this.destroy();
        return false;
      }
    } catch {
      this.destroy();
      return false;
    }
    gl.useProgram(this.program);
    this.buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
    gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),
      gl.STATIC_DRAW
    );
    const loc = gl.getAttribLocation(this.program!, "a_pos");
    gl.enableVertexAttribArray(loc);
    gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
    this.locs.clear();
    return true;
  }

  private loc(name: string): WebGLUniformLocation | null {
    if (!this.locs.has(name)) {
      this.locs.set(name, this.gl!.getUniformLocation(this.program!, name));
    }
    return this.locs.get(name) ?? null;
  }

  f(name: string, x: number) {
    this.gl?.uniform1f(this.loc(name), x);
  }
  i(name: string, x: number) {
    this.gl?.uniform1i(this.loc(name), x);
  }
  v2(name: string, x: number, y: number) {
    this.gl?.uniform2f(this.loc(name), x, y);
  }
  v3(name: string, c: RGB) {
    this.gl?.uniform3f(this.loc(name), c[0], c[1], c[2]);
  }

  draw(pixelW: number, pixelH: number) {
    const gl = this.gl;
    if (!gl || !this.program) return;
    gl.viewport(0, 0, pixelW, pixelH);
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  }

  destroy() {
    const gl = this.gl;
    if (!gl) return;
    if (this.buffer) gl.deleteBuffer(this.buffer);
    if (this.program) gl.deleteProgram(this.program);
    if (this.vs) gl.deleteShader(this.vs);
    if (this.fs) gl.deleteShader(this.fs);
    this.buffer = null;
    this.program = null;
    this.vs = null;
    this.fs = null;
    this.locs.clear();
    this.gl = null;
  }
}

// ---------------------------------------------------------------------------
// Real numbers (docs/specs/r10/magnetron-racetrack-sweep.md):
//  - racetrack radius oscillates between 0.3 and 0.7 of target radius
//  - oscillation period: 22s
//  - racetrack ring width: 0.08 * targetRadius
//  - erosion accumulation rate: 0.002 depth-units/s wherever the ring
//    currently sits — monotonic, never decays
// ---------------------------------------------------------------------------
const RING_MID = 0.5; // (0.3 + 0.7) / 2
const RING_AMPLITUDE = 0.2; // (0.7 - 0.3) / 2
const SWEEP_PERIOD_S = 22;
const RING_WIDTH = 0.08;
const EROSION_RATE = 0.002; // depth-units/s within the band
const EROSION_BUCKETS = 256;
const WARMUP_STEP = 0.25;
const LIVE_WARMUP_S = 40; // "existing erosion groove faint" at t0
const STATIC_WARMUP_S = 480; // deep enough for the dual-band asymmetry to read at a glance
const STATIC_PHASE_S = SWEEP_PERIOD_S * 0.5; // sweep phase 0.5 -> "racetrack-mid"

function ringRAt(tSeconds: number): number {
  return RING_MID + RING_AMPLITUDE * Math.sin((2 * Math.PI * tSeconds) / SWEEP_PERIOD_S);
}

export function MagnetronRacetrackSweep({
  paused = false,
  children,
  className = "",
  style,
}: MagnetronRacetrackSweepProps) {
  const wrapRef = useRef<HTMLDivElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const uid = useId();
  const pausedRef = useRef(paused);
  pausedRef.current = paused;

  useEffect(() => {
    const wrap = wrapRef.current;
    const canvas = canvasRef.current;
    if (!wrap || !canvas) return;

    const surface = new GLSurface(canvas, FRAG_SRC);
    let raf = 0;
    let running = false;
    let staticMode = false;
    let cssW = 0;
    let cssH = 0;
    let dpr = 1;
    let disposed = false;
    let lastMs = performance.now();

    // persistent, never-cleared radial erosion buffer — 1D because the
    // mechanic itself is radially symmetric (see header comment).
    const depthBuf = new Float32Array(EROSION_BUCKETS);
    const uploadBuf = new Uint8Array(EROSION_BUCKETS);
    let erosionTex: WebGLTexture | null = null;

    let simTime = 0;

    let hoverAngle = 0;
    let pointerActive = 0; // eased 0..1
    let pointerTarget = 0;

    const SCALES = [1, 0.7];
    const BUDGET_OVER = 24;
    let scaleIdx = 0;
    let frameEma = 16.7;
    let overMs = 0;
    let underMs = 0;
    let upWindow = 8000;

    let c0: RGB = [0.03, 0.03, 0.03];
    let c1: RGB = [0.18, 0.18, 0.18];
    let c2: RGB = [0.56, 0.56, 0.56];
    let c3: RGB = [0.93, 0.93, 0.93];
    let c4: RGB = [1, 1, 1];
    let bg: RGB = [0, 0, 0];
    let bias = 0;
    let contrast = 1.15;

    const readColors = () => {
      const cs = getComputedStyle(document.documentElement);
      const bgTok = parseHex(cs.getPropertyValue("--background")) ?? [1, 1, 1];
      const fg = parseHex(cs.getPropertyValue("--foreground")) ?? [0.09, 0.09, 0.09];
      const muted = parseHex(cs.getPropertyValue("--ns-muted")) ?? [0.55, 0.55, 0.55];
      const border = parseHex(cs.getPropertyValue("--border")) ?? [0.18, 0.18, 0.18];
      const black: RGB = [0, 0, 0];
      const white: RGB = [1, 1, 1];
      bg = bgTok;
      if (luminance(bgTok) < 0.5) {
        c0 = mixRGB(bgTok, black, 0.5);
        c1 = mixRGB(border, bgTok, 0.15);
        c2 = muted;
        c3 = fg;
        c4 = mixRGB(fg, white, 0.82);
        bias = -0.06;
        contrast = 1.18;
      } else {
        c0 = mixRGB(fg, black, 0.32);
        c1 = mixRGB(fg, muted, 0.55);
        c2 = mixRGB(muted, bgTok, 0.6);
        c3 = mixRGB(bgTok, muted, 0.16);
        c4 = bgTok;
        bias = 0.03;
        contrast = 1.2;
      }
    };
    readColors();

    // advance the erosion field by dt at absolute sim time t — pure function
    // of the sweep clock, no randomness, so warmup and the live loop use the
    // exact same function and a reduced-motion replay is deterministic.
    const simulate = (dt: number, t: number) => {
      const ringR = ringRAt(t);
      const half = RING_WIDTH / 2;
      const lo = ringR - half;
      const hi = ringR + half;
      const loIdx = Math.max(0, Math.floor(lo * EROSION_BUCKETS));
      const hiIdx = Math.min(EROSION_BUCKETS - 1, Math.ceil(hi * EROSION_BUCKETS));
      for (let i = loIdx; i <= hiIdx; i++) {
        depthBuf[i] += EROSION_RATE * dt;
      }
    };

    const warmup = (seconds: number) => {
      depthBuf.fill(0);
      let t = 0;
      while (t < seconds) {
        const step = Math.min(WARMUP_STEP, seconds - t);
        simulate(step, t);
        t += step;
      }
    };

    const uploadTexture = () => {
      const gl = surface.gl;
      if (!gl) return;
      for (let i = 0; i < EROSION_BUCKETS; i++) {
        const v = Math.max(0, Math.min(1.4, depthBuf[i]!)) / 1.4;
        uploadBuf[i] = Math.round(v * 255);
      }
      if (!erosionTex) {
        erosionTex = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, erosionTex);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
      } else {
        gl.bindTexture(gl.TEXTURE_2D, erosionTex);
      }
      gl.texImage2D(
        gl.TEXTURE_2D,
        0,
        gl.LUMINANCE,
        EROSION_BUCKETS,
        1,
        0,
        gl.LUMINANCE,
        gl.UNSIGNED_BYTE,
        uploadBuf
      );
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, erosionTex);
    };

    const draw = (t: number) => {
      if (!surface.gl || cssW <= 0 || cssH <= 0) return;
      uploadTexture();
      const targetR = Math.min(cssW, cssH) * 0.42;
      surface.v2("u_size", cssW, cssH);
      surface.f("u_dpr", dpr);
      surface.v2("u_center", cssW / 2, cssH / 2);
      surface.f("u_targetR", targetR);
      surface.f("u_erosionN", EROSION_BUCKETS);
      surface.f("u_ringR", ringRAt(t));
      surface.f("u_ringHalfWidth", RING_WIDTH / 2);
      surface.f("u_pointerAngle", hoverAngle);
      surface.f("u_pointerActive", pointerActive);
      surface.f("u_time", t);
      surface.v3("u_c0", c0);
      surface.v3("u_c1", c1);
      surface.v3("u_c2", c2);
      surface.v3("u_c3", c3);
      surface.v3("u_c4", c4);
      surface.v3("u_bg", bg);
      surface.f("u_bias", bias);
      surface.f("u_contrast", contrast);
      surface.i("u_erosion", 0);
      surface.draw(canvas.width, canvas.height);
    };

    const loop = (nowMs: number) => {
      const rawMs = nowMs - lastMs;
      const dt = Math.min(0.05, Math.max(0, rawMs / 1000));
      lastMs = nowMs;
      simTime += dt;

      pointerActive += (pointerTarget - pointerActive) * (1 - Math.exp(-dt / 0.25));

      simulate(dt, simTime);
      draw(simTime);

      const clamped = Math.min(50, rawMs);
      frameEma += (clamped - frameEma) * (1 - Math.exp(-clamped / 120));
      if (frameEma > BUDGET_OVER) {
        overMs += clamped;
        underMs = 0;
      } else {
        underMs += clamped;
        overMs = 0;
      }
      const down = overMs > 900 && scaleIdx < SCALES.length - 1;
      const up = underMs > upWindow && scaleIdx > 0;
      if (down || up) {
        scaleIdx += down ? 1 : -1;
        if (down) upWindow = Math.min(64000, upWindow * 2);
        overMs = 0;
        underMs = 0;
        frameEma = 16.7;
        applyBacking();
      }
      raf = requestAnimationFrame(loop);
    };
    const wake = () => {
      if (running || disposed) return;
      running = true;
      lastMs = performance.now();
      raf = requestAnimationFrame(loop);
    };
    const sleep = () => {
      cancelAnimationFrame(raf);
      running = false;
    };

    const applyBacking = () => {
      if (cssW < 2 || cssH < 2) return;
      dpr = Math.min(window.devicePixelRatio || 1, 1.5) * SCALES[scaleIdx]!;
      const pw = Math.round(cssW * dpr);
      const ph = Math.round(cssH * dpr);
      if (canvas.width !== pw || canvas.height !== ph) {
        canvas.width = pw;
        canvas.height = ph;
      }
      canvas.style.width = `${cssW}px`;
      canvas.style.height = `${cssH}px`;
      draw(staticMode ? STATIC_PHASE_S : simTime);
    };

    const resize = () => {
      const rect = wrap.getBoundingClientRect();
      if (rect.width < 2 || rect.height < 2) return;
      cssW = rect.width;
      cssH = rect.height;
      scaleIdx = 0;
      overMs = 0;
      underMs = 0;
      upWindow = 8000;
      frameEma = 16.7;
      applyBacking();
    };

    let rectLeft = 0;
    let rectTop = 0;
    let rectDirty = true;
    const syncRect = () => {
      if (!rectDirty) return;
      const rect = wrap.getBoundingClientRect();
      rectLeft = rect.left;
      rectTop = rect.top;
      rectDirty = false;
    };
    const markRectDirty = () => {
      rectDirty = true;
    };

    // pointer only sets WHERE the ring reads brighter (angle), never the
    // sweep clock — the sweep is a fixed physical cycle, not pointer-driven.
    const onPointerMove = (e: PointerEvent) => {
      syncRect();
      const cx = rectLeft + cssW / 2;
      const cy = rectTop + cssH / 2;
      const dx = e.clientX - cx;
      const dy = e.clientY - cy;
      hoverAngle = Math.atan2(dx, -dy);
      pointerTarget = 1;
      if (staticMode) draw(STATIC_PHASE_S);
    };
    const onPointerLeave = () => {
      pointerTarget = 0;
    };

    if (!surface.init()) return; // no WebGL: children still render over the page bg
    const ro = new ResizeObserver(resize);
    ro.observe(wrap);
    resize();

    wrap.addEventListener("pointermove", onPointerMove);
    wrap.addEventListener("pointerleave", onPointerLeave);
    window.addEventListener("scroll", markRectDirty, { passive: true, capture: true });
    window.addEventListener("resize", markRectDirty, { passive: true });

    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    let reduced = mq.matches;
    const applyMode = () => {
      if (reduced || pausedRef.current) {
        staticMode = true;
        sleep();
        warmup(STATIC_WARMUP_S);
        draw(STATIC_PHASE_S);
      } else {
        staticMode = false;
        simTime = LIVE_WARMUP_S;
        warmup(LIVE_WARMUP_S);
        wake();
      }
    };
    const onMq = () => {
      reduced = mq.matches;
      applyMode();
    };
    mq.addEventListener("change", onMq);

    let onScreen = true;
    const io = new IntersectionObserver(
      (entries) => {
        onScreen = entries.some((en) => en.isIntersecting);
        if (!onScreen) sleep();
        else if (!staticMode && !document.hidden) wake();
      },
      { threshold: 0 }
    );
    io.observe(wrap);

    const onVis = () => {
      if (document.hidden) sleep();
      else if (!staticMode && onScreen) wake();
    };
    document.addEventListener("visibilitychange", onVis);
    applyMode();

    let lastPolledPaused = pausedRef.current;
    let poll = 0;
    const tick = () => {
      if (pausedRef.current !== lastPolledPaused) {
        lastPolledPaused = pausedRef.current;
        applyMode();
      }
      poll = window.setTimeout(tick, 140);
    };
    tick();

    const themeObserver = new MutationObserver(() => {
      readColors();
      if (staticMode) draw(STATIC_PHASE_S);
    });
    themeObserver.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"],
    });

    const onLost = (e: Event) => {
      e.preventDefault();
      sleep();
    };
    const onRestored = () => {
      erosionTex = null;
      if (surface.init()) {
        resize();
        applyMode();
      }
    };
    canvas.addEventListener("webglcontextlost", onLost);
    canvas.addEventListener("webglcontextrestored", onRestored);

    return () => {
      disposed = true;
      ro.disconnect();
      io.disconnect();
      mq.removeEventListener("change", onMq);
      document.removeEventListener("visibilitychange", onVis);
      themeObserver.disconnect();
      canvas.removeEventListener("webglcontextlost", onLost);
      canvas.removeEventListener("webglcontextrestored", onRestored);
      wrap.removeEventListener("pointermove", onPointerMove);
      wrap.removeEventListener("pointerleave", onPointerLeave);
      window.removeEventListener("scroll", markRectDirty, { capture: true } as EventListenerOptions);
      window.removeEventListener("resize", markRectDirty);
      window.clearTimeout(poll);
      sleep();
      if (erosionTex && surface.gl) surface.gl.deleteTexture(erosionTex);
      erosionTex = null;
      surface.destroy();
    };
  }, []);

  return (
    <div
      ref={wrapRef}
      data-magnetron-racetrack-sweep={uid}
      className={`relative isolate h-full w-full touch-none overflow-hidden bg-background ${className}`}
      style={style}
    >
      <canvas ref={canvasRef} aria-hidden="true" className="absolute inset-0 block h-full w-full" />
      {children ? <div className="relative z-[1] h-full w-full">{children}</div> : null}
    </div>
  );
}

MagnetronRacetrackSweep.displayName = "MagnetronRacetrackSweep";
Build spec

Build <MagnetronRacetrackSweep paused? children? className?> as a full-bleed WebGL background rendering sputter-target erosion under an oscillating magnetron racetrack. THE EROSION FIELD IS 1D: because the racetrack is a full annulus at every instant, its erosion history is a function of radius alone, never angle — the persistent, never-cleared accumulation buffer (same non-clearing technique as rime-creep/edm-crater-field, but MONOTONIC here, never decaying, matching real physical erosion) is therefore a 256-texel 1D LUMINANCE texture (EROSION_BUCKETS=256), not a full 2D grid. ringRAt(t) = 0.5 + 0.2*sin(2*pi*t/22) is the sweep's live normalized radius (oscillating between 0.3 and 0.7 of target radius, 22s period); every simulate(dt,t) call adds EROSION_RATE=0.002 depth-units/s to every bucket whose normalized radius falls within [ringRAt(t) - 0.04, ringRAt(t) + 0.04] (RING_WIDTH=0.08 total). Because the sinusoid dwells longest at its own turning points (0.3 and 0.7) and passes through the middle twice per cycle at full speed, the two turning-point bands erode measurably deeper than the middle band with ZERO extra logic — the dual-band asymmetry is the sweep's own kinematics, not a scripted bias. GROOVE SHADING: the composite fragment shader derives a surface normal from the 1D depth field's RADIAL gradient only (central difference at r +/- 1/256, no tangential term exists by construction) and projects it back into 2D along the local radial direction (rDir = p/length(p)), so the groove reads as a real machined cut catching a small achromatic multi-band studio (env(), same family as edm-crater-field's, fewer bands than a mirror pool needs) rather than a flat painted ring. Material removed subtracts directly from luminance (depthVis*0.34) — lower relative luminance than the fresh face, same direction in both themes, only bias/contrast differ per theme (dark: bias -0.06, contrast 1.18; light: bias 0.03, contrast 1.2). LIVE GLOW RING: a second, non-accumulated quantity — ringMask, a smoothstep band around the CURRENT ringRAt(t), added straight to luminance (+0.5) every frame, explicitly never colour (real sputtering plasma glows violet/pink; that hue is discarded on purpose per the monochrome constraint) and never composited into the persistent erosion texture. POINTER: pointermove computes the angle from the target centre to the pointer (never its radius) and eases pointerActive 0->1 over a 250ms exponential; the shader adds an angular gaussian boost (falloff 0.35 rad) on top of ringMask, brightening the glow ring locally by luminance only, never accent, and it must never alter ringRAt/simTime — the sweep is a fixed physical cycle, not pointer-driven. HOST: mirrors edm-crater-field's scaffolding — a GLSurface host class (fullscreen triangle pair, lazily-resolved uniform locations), a two-tier adaptive DPR ladder (SCALES=[1,0.7], steps down after a sustained >24ms EMA for 900ms, back up after a much longer clean stretch that doubles each time it trips), DPR capped at 1.5, a ResizeObserver on the wrapper (never window.resize), IntersectionObserver(threshold 0) + visibilitychange both gating the single rAF loop, webglcontextlost/restored rebuilding cleanly, and a paused prop polled every 140ms (props can't be read fresh inside a long-lived rAF closure without a ref). Tokens (--background, --foreground, --ns-muted, --border) are read via getComputedStyle(document.documentElement) before any paint and re-read on a MutationObserver watching documentElement's class. prefers-reduced-motion (or paused) sets a STATIC_WARMUP_S=480s deterministic warmup (the same simulate() function stepped in fixed 0.25s chunks from an all-zero buffer, no randomness anywhere in this component) then draws exactly one frame at STATIC_PHASE_S = 22*0.5 (sweep phase 0.5, ring at mid-radius, dual-band asymmetry already visible), named "racetrack-mid" — never a blank disc. The live (non-reduced) mode instead warms up only LIVE_WARMUP_S=40s before its first paint, so the groove starts faint rather than either pristine or already deep.

Props

PropTypeDefaultDescription
paused?booleanfalseFreezes the surface on a composed steady-state frame without unmounting.
children?React.ReactNodeRendered in the DOM over the field — eyebrow, headline, CTA.
className?string
style?React.CSSProperties