{
  "schemaVersion": 1,
  "id": "op-art-tunnel",
  "version": "1.0.0",
  "status": "published",
  "kind": "background",
  "title": "Op Art Tunnel",
  "summary": "A black-and-white optical-illusion tunnel of counter-turning checked rings that seems to ripple and bulge. Loops perfectly.",
  "description": "An optical illusion in the manner of the 1960s: a tunnel of black-and-white checks in which every other ring turns the opposite way, so the whole surface seems to ripple, breathe and bulge though nothing but flat tiles is moving. The rings race towards you and the checkerboard turns by whole pairs of tiles. Choose a round or square tunnel, the tones (classic, red, blue or gold), the number of tiles round each ring, how many rings pass per loop, the turn, the ring density and the shading.",
  "role": "Shader background (three.js): a hypnotic, graphic op-art loop for fashion, music and bold design content.",
  "collections": [
    "backgrounds",
    "hypnotic-loops"
  ],
  "tags": [
    "op art",
    "optical illusion",
    "tunnel",
    "checkerboard",
    "black and white",
    "hypnotic",
    "1960s",
    "pattern",
    "geometric",
    "loop"
  ],
  "synonyms": [
    "optical illusion tunnel",
    "checkerboard tunnel",
    "vasarely",
    "bridget riley",
    "moire tunnel",
    "mod pattern"
  ],
  "useCases": [
    "A fashion, music or art-house title backdrop",
    "A bold graphic transition",
    "A hypnotic illusion loop"
  ],
  "featured": null,
  "publishedAt": "2026-10-06",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/op-art-tunnel.js",
    "exports": [
      {
        "name": "createOpArtTunnel",
        "signature": "createOpArtTunnel({ shape?, palette?, tiles?, rush?, swirl?, density?, contrast? }) => { scene, camera, setSize(width, height), update(time, loopSeconds), dispose() }",
        "description": "The tunnel as one full-screen shader; render its scene with its camera."
      },
      {
        "name": "PALETTES",
        "signature": "PALETTES: { classic, red, blue, gold }",
        "description": "Dark, light and accent colours."
      }
    ],
    "reusable": [
      "src/op-art-tunnel.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "three",
      "version": "0.185.1",
      "reason": "Renderer and a full-screen shader material."
    },
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "scope": "demo",
      "reason": "Demo harness."
    }
  ],
  "params": [
    {
      "key": "shape",
      "label": "Tunnel",
      "type": "select",
      "default": "round",
      "options": [
        {
          "value": "round",
          "label": "Round"
        },
        {
          "value": "square",
          "label": "Square"
        }
      ]
    },
    {
      "key": "palette",
      "label": "Colours",
      "type": "select",
      "default": "classic",
      "options": [
        {
          "value": "classic",
          "label": "Black and white"
        },
        {
          "value": "red",
          "label": "Red glow"
        },
        {
          "value": "blue",
          "label": "Blue"
        },
        {
          "value": "gold",
          "label": "Gold"
        }
      ]
    },
    {
      "key": "tiles",
      "label": "Tiles per ring",
      "type": "integer",
      "min": 8,
      "max": 40,
      "step": 2,
      "default": 16
    },
    {
      "key": "rush",
      "label": "Rings per loop",
      "help": "Rings passing per loop (even).",
      "type": "integer",
      "min": 2,
      "max": 8,
      "step": 2,
      "default": 4
    },
    {
      "key": "swirl",
      "label": "Turn",
      "help": "Pairs of tiles the rings turn per loop.",
      "type": "integer",
      "min": 0,
      "max": 3,
      "step": 1,
      "default": 1
    },
    {
      "key": "density",
      "label": "Ring density",
      "type": "number",
      "min": 0.5,
      "max": 2,
      "step": 0.1,
      "default": 1
    },
    {
      "key": "contrast",
      "label": "Ring shading",
      "type": "number",
      "min": 0,
      "max": 1,
      "step": 0.1,
      "default": 0.6
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Round, black and white",
      "params": {}
    },
    {
      "id": "square",
      "label": "Square, red glow",
      "params": {
        "shape": "square",
        "palette": "red",
        "tiles": 24
      }
    },
    {
      "id": "blue",
      "label": "Blue, fine tiles",
      "params": {
        "palette": "blue",
        "tiles": 32,
        "density": 1.4,
        "swirl": 2
      }
    },
    {
      "id": "gold",
      "label": "Gold, bold",
      "params": {
        "palette": "gold",
        "tiles": 10,
        "rush": 2,
        "contrast": 0.9
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 12,
    "posterSeconds": 3,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 12,
      "method": "The rings race a whole even number of rings per loop (so every other ring still turns the same way) and the tiles turn by whole pairs of tiles, so the last frame meets the first."
    },
    "seed": null
  },
  "composition": {
    "inputs": "Time within the loop; the tunnel shape, tones, tiles, rings per loop, turn, density and shading.",
    "outputs": "A full-frame shader image.",
    "coordinates": "Screen-centred, ±1 vertically; the tunnel's axis points into the screen at the centre; distance down it is 1.2 / radius (or / the largest of |x| and |y| for a square tunnel).",
    "lighting": "Flat tones with a soft shade at each ring's edge and an accent glow at the vanishing point.",
    "timing": "update(time, loopSeconds) sets the loop phase; frames render in any order.",
    "compatibleWith": [
      "kinetic-title",
      "number-reveal"
    ],
    "notes": [
      "Colour is a checkerboard of ring number plus tile number, so all the motion is in the tiles' positions."
    ]
  },
  "reuse": [
    "Art-house title: Round, black and white.",
    "Fashion: Square, red glow.",
    "Calm: Blue, fine tiles."
  ],
  "integration": {
    "newProject": "Create a WebGLRenderer and createOpArtTunnel(); call tunnel.setSize(w, h) and each frame tunnel.update(time, loopSeconds) then renderer.render(tunnel.scene, tunnel.camera).",
    "existingProject": "Copy src/op-art-tunnel.js and render tunnel.scene with tunnel.camera into your own target."
  },
  "performance": {
    "notes": "One full-screen fragment shader with a few arithmetic operations: under 1 ms a frame at 1080² on an integrated Intel GPU.",
    "limits": "Software WebGL renders in about a second a frame."
  },
  "limitations": [
    "Strongly flickering checkerboards can be uncomfortable for some viewers; use a low ring count and a low tile count for a gentler look.",
    "Tiles near the middle are smaller than a pixel, which shows as a grey haze."
  ],
  "license": {
    "spdx": "MIT",
    "notice": "Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.",
    "thirdParty": []
  },
  "provenance": "Adapted from the owner's threejs-sketches/shaders-backgrounds/op-art-tunnel (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original shader: a checkerboard of ring and tile numbers with alternate rings counter-turning, racing an even number of rings per loop, on an exact loop.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-05",
      "notes": "Initial release."
    }
  ]
}
