{
  "schemaVersion": 1,
  "id": "ceiling-fan-wipe",
  "version": "1.0.0",
  "status": "published",
  "kind": "effect",
  "title": "Ceiling Fan Wipe",
  "summary": "The overhead ceiling fan transition from TikTok: a slowly turning fan right under the camera, and every time a blade sweeps across, the picture below has changed.",
  "description": "The ceiling fan trend, rebuilt as a transition you can reuse. The camera looks straight down past a ceiling fan; each time a blade passes over the subject, the look underneath changes, and the cut is always hidden under the blade. The fan is drawn entirely in code: wood-grain or painted blades on metal blade irons, a brass, nickel, black or white motor cap, real motion blur, a soft focus because the blades are close to the lens, and a soft shadow on the floor. Choose the number of blades, the finish, how often the look changes, which way it turns and whether the new look follows the blade (sweep) or appears at once (cut). The demo wipes between four floors under window light; in your project the looks can be anything you can draw: outfits, products, photos, video frames or other ingredients.",
  "role": "Full-frame transition (Canvas 2D): switches between looks you draw, hidden behind a moving ceiling fan in the foreground.",
  "collections": [
    "product-reveals",
    "hypnotic-loops"
  ],
  "tags": [
    "ceiling fan",
    "fan",
    "transition",
    "wipe",
    "outfit change",
    "outfit transition",
    "overhead",
    "top down",
    "birds eye",
    "lookbook",
    "ootd",
    "before and after",
    "makeover",
    "tiktok trend",
    "reels",
    "blades",
    "motion blur",
    "2d",
    "canvas",
    "loop"
  ],
  "synonyms": [
    "ceiling fan trend",
    "ceiling fan transition",
    "fan transition trend",
    "AI ceiling fan trend",
    "fan blade wipe",
    "fan wipe transition",
    "under the fan video",
    "outfit change transition",
    "fan outfit change",
    "blade wipe",
    "overhead fan shot"
  ],
  "useCases": [
    "Outfit-of-the-day or lookbook reel where every blade pass is a new outfit",
    "Interior makeover: floors, rugs or room styles swapping under the fan",
    "Product colourway reveal shot from above",
    "Before-and-after transitions for food, flat lays or desk setups"
  ],
  "featured": null,
  "publishedAt": "2026-10-03",
  "implementation": {
    "target": "canvas2d",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/ceiling-fan-wipe.js",
    "exports": [
      {
        "name": "createCeilingFanWipe",
        "signature": "createCeilingFanWipe({ blades?, hub?, hubSize?, reach?, bladeWidth?, finish?, metal?, passes?, direction?, shutter?, samples?, focusBlur?, shadow?, shadowOffset?, lightAngle?, subject?, mode?, seed? }) => { draw(ctx, info, width, height, drawLook), drawFan(ctx, info, width, height), angleAt(time, duration, width?, height?), lookAt(time, duration), passes, blades }",
        "description": "Canvas 2D transition. draw() calls drawLook(ctx, index, info, width, height) for the looks it needs (the current one and, while a blade is crossing, its neighbours, clipped along the blade), then draws the fan shadow, blurred blades and motor cap on top. lookAt() tells you which look is at the subject, for syncing sound or captions."
      }
    ],
    "reusable": [
      "src/ceiling-fan-wipe.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "scope": "demo",
      "reason": "Demo harness and seeded random numbers for the floors. src/ceiling-fan-wipe.js has no dependencies."
    }
  ],
  "params": [
    {
      "key": "finish",
      "label": "Blade finish",
      "type": "select",
      "default": "oak",
      "options": [
        {
          "value": "oak",
          "label": "Oak"
        },
        {
          "value": "walnut",
          "label": "Walnut"
        },
        {
          "value": "black",
          "label": "Matte black"
        },
        {
          "value": "white",
          "label": "White"
        }
      ]
    },
    {
      "key": "metal",
      "label": "Motor and arms",
      "type": "select",
      "default": "brass",
      "options": [
        {
          "value": "brass",
          "label": "Brass"
        },
        {
          "value": "nickel",
          "label": "Brushed nickel"
        },
        {
          "value": "black",
          "label": "Black"
        },
        {
          "value": "white",
          "label": "White"
        }
      ]
    },
    {
      "key": "blades",
      "label": "Blades",
      "type": "integer",
      "min": 3,
      "max": 6,
      "step": 1,
      "default": 5
    },
    {
      "key": "passes",
      "label": "Changes per loop",
      "help": "How many times a blade passes and the look changes in the 8-second loop. 8 is one change a second.",
      "type": "integer",
      "min": 4,
      "max": 24,
      "step": 4,
      "default": 8
    },
    {
      "key": "hub",
      "label": "Fan position",
      "help": "Corner: the hub sits in the top-left corner and the blades sweep diagonally across. Middle: the fan hangs over the centre and the whole picture cuts as a blade crosses the subject.",
      "type": "select",
      "default": "corner",
      "options": [
        {
          "value": "corner",
          "label": "Hub in the corner"
        },
        {
          "value": "centre",
          "label": "Hub in the middle"
        }
      ]
    },
    {
      "key": "wipe",
      "label": "Change style",
      "help": "Sweep: the new look follows the blade across the frame. Cut: the whole picture changes the moment a blade covers the subject. With the hub in the middle it always cuts.",
      "type": "select",
      "default": "sweep",
      "options": [
        {
          "value": "sweep",
          "label": "Sweep with the blade"
        },
        {
          "value": "cut",
          "label": "Cut under the blade"
        }
      ]
    },
    {
      "key": "direction",
      "label": "Turning",
      "type": "select",
      "default": "clockwise",
      "options": [
        {
          "value": "clockwise",
          "label": "Clockwise"
        },
        {
          "value": "anticlockwise",
          "label": "Anticlockwise"
        }
      ]
    },
    {
      "key": "motionBlur",
      "label": "Motion blur",
      "help": "Blur along the turn, as a fraction of a frame. 0 is razor sharp.",
      "type": "number",
      "min": 0,
      "max": 1,
      "step": 0.05,
      "default": 0.5
    },
    {
      "key": "softness",
      "label": "Blade softness",
      "help": "Out-of-focus blur on the fan, because it is close to the lens (pixels at 1080).",
      "type": "number",
      "min": 0,
      "max": 12,
      "step": 0.5,
      "default": 3
    },
    {
      "key": "shadow",
      "label": "Shadow on the floor",
      "type": "number",
      "min": 0,
      "max": 0.8,
      "step": 0.05,
      "default": 0.35
    },
    {
      "key": "captions",
      "label": "Captions",
      "help": "Number and name each look in the corner, lookbook style.",
      "type": "boolean",
      "default": true
    },
    {
      "key": "seed",
      "label": "Variation",
      "help": "Changes the wood grain and the floor details.",
      "type": "integer",
      "min": 1,
      "max": 9999,
      "step": 1,
      "default": 3
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Oak and brass",
      "params": {}
    },
    {
      "id": "matte-black",
      "label": "Matte black, three blades",
      "params": {
        "finish": "black",
        "metal": "black",
        "blades": 3,
        "passes": 12,
        "motionBlur": 0.7,
        "shadow": 0.45
      }
    },
    {
      "id": "white-centre",
      "label": "White fan overhead, quick cuts",
      "params": {
        "finish": "white",
        "metal": "white",
        "blades": 4,
        "hub": "centre",
        "passes": 16,
        "direction": "anticlockwise",
        "softness": 4,
        "captions": false
      }
    },
    {
      "id": "walnut-nickel",
      "label": "Walnut and nickel, slow",
      "params": {
        "finish": "walnut",
        "metal": "nickel",
        "passes": 4,
        "softness": 2
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 8,
    "posterSeconds": 1.6,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 8,
      "method": "The fan turns a whole number of blade spacings per loop (Changes per loop) and every blade is identical, so the last frame meets the first. Changes per loop is always a multiple of 4, the number of demo looks, so the loop also ends on the look it started with."
    },
    "seed": {
      "param": "seed",
      "behavior": "Seed shapes the wood grain and the floor details (plank tones, terrazzo chips, marble veins). Same seed, same picture."
    }
  },
  "composition": {
    "inputs": "Canvas 2D context, width, height, info from the harness, and a drawLook(ctx, index, info, width, height) callback that fills the frame with look number index (it may be negative or past your count; wrap it).",
    "outputs": "Opaque frame: the looks underneath, the fan shadow, then the blades and motor cap.",
    "coordinates": "hub and subject are fractions of width and height from the top left; sizes are fractions of the shorter side. Angles in radians, clockwise on screen.",
    "lighting": "The fan carries its own light: a sheen on the blades that brightens as they turn toward the light direction (lightAngle), lit blade edges, a metal cap with a glint, and a soft floor shadow offset by shadowOffset. Match lightAngle and shadowOffset to the light in your looks.",
    "timing": "Pure function of info.time and info.duration. The look index changes exactly when a blade centre line crosses the subject; lookAt(time, duration) returns it, starting at 0.",
    "compatibleWith": [
      "gradient-background",
      "kinetic-title",
      "dither-animation",
      "stop-motion-feel",
      "particle-tunnel"
    ],
    "notes": [
      "Sweep needs the hub near a corner or edge, so only one blade crosses the frame at a time. With the hub in the middle use mode cut.",
      "drawLook may be called up to three times a frame while a blade crosses; pre-render static looks to canvases like the demo does.",
      "Motion blur draws the blades once per sample (8 by default) into an internal layer; lower samples for a faster live preview."
    ]
  },
  "reuse": [
    "Lookbook reel: make drawLook draw photo or video frame number index of your outfits, set passes to the number of outfits (or a multiple of it) and use lookAt to put the right caption on each.",
    "Colourway reveal: draw the same product shot from above in four colours, or four presets of Soft Moving Gradient with Kinetic Title Reveal on top, and let the fan wipe between them.",
    "Use drawFan alone over any scene for the overhead fan as a foreground layer, then run Dither Animation's applyToCanvas over the result for a lo-fi version of the trend."
  ],
  "integration": {
    "newProject": "Create the transition once with createCeilingFanWipe(options). Each frame call fan.draw(ctx, info, width, height, drawLook), where drawLook fills the frame with your look number index.",
    "existingProject": "Copy src/ceiling-fan-wipe.js. Move your current frame drawing into drawLook, branching on the index, and call fan.draw(...) instead; or draw your scene as usual and call fan.drawFan(ctx, info, width, height) at the end for the fan without the wipe. info needs time, duration and fps."
  },
  "performance": {
    "notes": "Per frame: up to three looks, 8 motion-blur samples of the blades (one full-frame layer each) and two blurred composites for the shadow and the soft focus. The blade texture is computed once per output size. The live preview on aipixelwizard.com uses 4 motion-blur samples; renders and exports use 8.",
    "limits": "Motion blur and the blur filter dominate the cost; for a live preview at 4K or on phones pass samples: 4 and focusBlur: 0."
  },
  "limitations": [
    "It is the transition and the fan, not the person: the outfits, products or photos underneath are yours to supply (the trend itself uses AI to restyle one photo).",
    "Seen only from straight under the ceiling; there is no tilted or perspective camera.",
    "Sweep assumes the hub sits near a corner or an edge; with the hub in the middle use cut, as real edits do.",
    "Blur uses the canvas filter property; browsers without it (Safari before 18) show sharp blades and a hard shadow."
  ],
  "license": {
    "spdx": "MIT",
    "notice": "Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.",
    "thirdParty": []
  },
  "provenance": "Original code written for AI Pixel Wizard on 2026-10-03 by an automated agent (the daily trend routine), inspired by the AI ceiling fan transition trend on TikTok and Reels (see content/trends/2026-10-03-ceiling-fan-wipe.md). The blade-as-wipe idea is the trend itself, as described by The Tab (https://thetab.com/2026/09/28/how-to-do-the-vibey-ai-ceiling-fan-trend-from-tiktok-where-your-outfit-changes) and Pixazo (https://www.pixazo.ai/trending/fan-transition-trend); the fan, wood grain, motion blur, wipe geometry and floors are generated by this code. No code, images, fonts or shaders were copied.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-03",
      "notes": "Initial release."
    }
  ]
}
