A golden-hour drone shot over a sand sea: the camera skims a rippled windward slope, climbs through sand streaming off a razor crest, and rises to reveal gold faces and lilac slip faces stacked into haze as long crest shadows stretch across the corridors.
Written and rendered by AI CSS Animations Oct 18, 2026
Wind-built dunes have a gentle windward slope, up which sand grains hop and roll, and a steep lee side called the slip face, where sand piles up until it avalanches at the angle of repose, about 30 to 34 degrees. Small wind ripples cover the windward slopes and creep downwind, while strong wind sends sand streaming off the sharp crests. Low sun splits each dune into a lit face and a shadowed one, which is why photographers and drone filmmakers work sand seas in the early morning and late afternoon. The look is widely used in travel, automotive and fashion films.
Make a 10-second drone shot of sand dunes at golden hour: the camera skims up a rippled slope, crosses a sharp crest streaming sand, and reveals a dune sea in long shadows. <inputs> If I don't give you something, use the default and don't ask. - Title: "Kessar" - Line: "A sand sea at golden hour · wind 9 m/s" (fictional) - Sun: 8° above the horizon at the start, sinking to 6°. - Length: 10 seconds. </inputs> <direction> - Look: low sun from the left splits every dune into a gold windward face and a cool lilac slip face, with the crest drawn as one clean line between them. Shadows take the blue of the sky, never black. - Palette: sky #F6C98A at the horizon through #E9B2A0 to #A9B4D1 at the top; sand lit #F2B66B to highlights #FFD9A0; sand mid #C98A4E; slip faces in shade #7C6A94 to #5B4F7A; distant dunes melting into haze #EDBF96; blowing sand #FFE2B0; title #2E2238, line #5B4A5E. - Dunes, built the way wind builds them, with the wind blowing from the sun's side: a transverse dune field on a 3 km plane with 1,024 × 1,024 segments, its height one closed-form function: crests about 180 m apart and 25–40 m high, crest lines made sinuous by seeded fBm warping (amplitude 40 m), with a few Y-junctions where two crests join. Across each dune, a gentle windward slope of 10–12° rises to a sharp brink and drops down a slip face at the angle of repose, about 32°, into a flat corridor. Smooth only the toe of each slip face, never the crest. Small secondary dunes ride the windward slopes. The fragment shader takes normals from the analytic function, so lighting stays smooth between vertices. - Ripples, in the fragment shader: wind ripples about 12 cm apart, perpendicular to the wind, gently sinuous with forks, asymmetric (steeper on their downwind side), only on windward slopes (fading out above 18° of slope); slip faces get fine downslope avalanche grooves instead. Ripples creep downwind at 4 cm/s. Fade them out where their frequency nears the pixel size (use fwidth), so there is never moiré. - Light and shadow: a DirectionalLight sun #FFB56B at intensity 3.2 from the left, 8° up and sinking to 6°. Terrain shadows come from the fragment shader marching the height function toward the sun (40 steps, growing step length, keeping the minimum clearance for a soft penumbra), so every crest throws a long shadow across the next corridor and the shadows visibly stretch as the sun sinks. HemisphereLight sky #9DB0DA, ground #E3A06A, at 1.1, so slip faces read lilac with detail. FogExp2 toward #EDBF96 so the dunes stack into haze. ACESFilmic, exposure 1.15. Every frame's average brightness stays between 35% and 60%. - Blowing sand: 60,000 grains as Points streaming off every crest within 300 m of the camera (find crest points at load by scanning the height function along the wind), carried downwind over the slip face, falling and thinning out within 15 m, bright #FFE2B0 where they rise into sunlight against the shaded slip face. A thin layer of saltating sand skims the windward slopes as a moving streak texture under 30 cm high at 3 m/s. Grain positions are closed-form functions of t from their seeded emission times. - Camera: fov 42, one continuous CatmullRom path on easeInOutSine. It starts 3 m above a windward slope, heading diagonally up it toward the crest, with the sun low to the left so the ripples rake into stripes of light and shade; glides upslope at 6 m/s, rising as it nears the brink; passes through the edge of the sand stream at the crest at 4.6 s, grains streaking close past the lens; then climbs to 45 m and turns to look along the dune field, so the crests recede diagonally to the horizon at y 330, each with its lit windward face to the left and its lilac slip face to the right. It never touches the sand. - Type (HTML over the canvas): title in Tenor Sans 400, 96px, uppercase, tracked 0.4em, #2E2238, baseline at (120, 200) on the sky; line in Onest 400, 22px, tracked 0.14em, #5B4A5E, beneath. Each rises 10px as it fades in over 0.8 s on cubic-bezier(0.22, 1, 0.36, 1). - Premium: crests as one sharp line between gold and lilac, ripples catching the low sun on windward slopes only, sand streaming off the brinks, haze stacking the dunes into the distance. Cheap: round sine-wave hills, an orange tint over everything, noise bumps and no slip faces. Banned: camels, palms, pyramids, people, vehicles or footprints, the sun disc or lens flares in frame, black shadows, heat-haze wobble over the whole frame, a sandstorm that hides the scene, anything flashing more than 3 times per second. </direction> <structure> - 0.0–1.2s: low over a rippled windward slope in gold light, the ripples drawn as fine stripes of light and shade, a thin skin of sand skimming across them. - 1.2–3.4s: the camera glides up the slope toward the brink; ahead, a plume of sand streams off the crest, bright against the sky. - 3.4–4.6s: the stream thickens as the camera rises to meet it and passes through its edge, grains streaking past. - 4.6–6.0s: over the crest the dune sea opens: lilac slip faces, long shadows across the corridors, crests stacked into haze, plumes streaming from the nearer ones; the title fades in from 4.9 s and the line from 5.2 s. - 6.0–8.8s: the camera eases into a slow forward drift; the sun sinks a little lower and the shadows keep stretching. - 8.8–10.0s: hold: the camera nearly still; only the blowing sand and creeping ripples move. For other lengths, stretch the climb and the drift; keep at least 1 s of hold. </structure> <build> - One self-contained index.html (inline CSS and JS), a 1920×1080 (16:9) stage scaled to fit the window, designed for 60fps. No image, video, texture or model files: every shape and texture is generated in code. - window.seek(t), t in seconds, computes every visual from t alone: no CSS transitions or CSS animations, no setTimeout/setInterval, no state carried between frames, randomness only from a seeded PRNG. Anything cumulative is solved analytically from t or precomputed once at load at a fixed step. Past the end, seek clamps to the last frame. - Without ?render in the URL, drive seek from requestAnimationFrame so it plays in a browser (loop after a 1s end hold); with ?render, don't start that loop. - Google Fonts via <link> are fine; await document.fonts.load() for each face before defining window.seek. - Engine: three.js as an ES module pinned to three@0.160.0 through an import map on jsDelivr (`three` and `three/addons/`). WebGLRenderer with antialias, preserveDrawingBuffer: true and setPixelRatio(window.devicePixelRatio); render once at the end of every seek. Text is HTML over the canvas. - Pass the height function's parameters to the shaders as uniforms and use the same function in JS to keep the camera above the sand. Shadows, ripples, grains and the camera are closed-form functions of t. </build> <check> Before showing me, step through one frame per second (t = 0…10): screenshot each and look at it. Fix overlaps, clipped or cramped text, and anything off-style or Banned. Check no frame has black or near-black areas, every crest is one sharp line with its slip face on the downwind side, ripples appear only on windward slopes with no moiré at 1280×720, sand streams leave the crests downwind, the camera never touches the sand, and the title sits on clear sky. Confirm seek(10) and seek(11) match, so the final frame holds. </check>
Ours, and free to use under CC0: copy it, change it, ship it. No credit needed.
A slow golden-hour glide down a faceted valley: flat-shaded ridges, a low sun, fog melting the far range into the horiz…
A device built in three.js rises out of total darkness while a softbox reflection glides along its titanium edge, then…
A toy-like isometric city block that builds itself on a floating slab: bevelled buildings rise floor by floor, cars loo…
Chunky two-tone 3D letters swing into place one by one on a seamless studio sweep, lit like a product shot with soft sh…
More like this on the motion design inspiration board: 3D motion design inspiration