Affect3d Gif May 2026
| Step | Description | |------|-------------| | | A rotating torus‑knot with a metallic‑red material. | | Lighting | Directional + ambient light for depth. | | Post‑processing | UnrealBloomPass adds a soft glow that gives the animation its “affective” feel. | | Capture | Every 1/30 second we pull the raw pixel buffer from the canvas and push it into gif.js . | | Export | Once 2 seconds of animation are recorded, the GIF is assembled and automatically downloaded. | Tip: Swap UnrealBloomPass for FilmPass , GlitchPass , or a custom shader to change the mood instantly. 4️⃣ Common Gotchas & How to Fix Them | Issue | Why it Happens | Fix | |-------|----------------|-----| | GIF appears jerky | Frame‑capture isn’t synchronized with the render loop. | Use requestAnimationFrame and a fixed‑time accumulator ( delta ) to guarantee exactly FPS captures per second. | | Colors look washed out | The canvas is using sRGB but gif.js expects linear RGB. | Add renderer.outputEncoding = THREE.sRGBEncoding; before recording, and call renderer.toneMapping = THREE.ACESFilmicToneMapping; for better contrast. | | File size > 5 MB | Too many frames or high resolution. | Reduce width/height ( renderer.setSize(480, 270) ), lower quality in GIF options, or use a palette‑reduction step ( gif.js does this automatically if quality ≤ 10). | | Loop doesn’t close perfectly | The first and last frames aren’t identical. | Make the animation mathematically periodic (e.g., use Math.sin(t) / Math.cos(t) ) or duplicate the first frame at the end. | | Browser freezes during encoding | Large GIFs block the main thread. | Increase workers in the GIF constructor (e.g., workers: 4 ) or off‑load the process to a Web Worker manually. | 5️⃣ Extending the Example | Feature | How to add it | |---------|---------------| | UI controls | Use a lightweight UI library like Tweakpane to expose bloom strength, rotation speed, or GIF length. | | Export to APNG | Replace gif.js with apngjs (same API, lossless transparency). | | Batch render | Wrap the code in a function that accepts a JSON description of multiple scenes, then loops over them, concatenating the resulting GIFs into a zip (use JSZip ). | | Server‑side rendering | For high‑resolution outputs (≥ 1080p) run the same Three.js code in Node + headless‑gl and pipe the frames into ffmpeg → MP4 → GIF. | | Interactive GIF | Record a short “preview” with the UI, then embed the GIF in a <canvas> that restores interactivity when the user clicks (swap the static GIF for the live Three.js canvas). | 6️⃣ Where to Learn More | Resource | Type | Highlights | |----------|------|------------| | Official Affect3D repo | GitHub (open‑source) | Boilerplates, docs, and a built‑in GIF recorder example. | | Three.js Journey (by Bruno Simon) | Paid video course | Deep dive into post‑processing pipelines, which translate directly to Affect3D. | | “Creating GIFs with WebGL” (MDN blog) | Article | Discusses pitfalls of pixel‑capture and cross‑browser considerations. | | “Animating with Bloom & Film Grain” (Medium) | Tutorial | Shows how to combine multiple passes for cinematic moods. | | gif.js documentation | API reference | Options for quality, worker count, and palette control. | Quick link: https://github.com/affect3d/affect3d (clone, npm i , npm start → you’ll see a live editor that already includes a “Export GIF” button.) 7️⃣ TL;DR – One‑Liner Cheat Sheet // After setting up your Three.js scene: const gif = new GIF(workers:2, quality:10); gif.addFrame(canvasCtx.getImageData(0,0,w,h), delay:1000/30); gif.on('finished',blob=>download(blob,'my‑loop.gif')); gif.render(); That’s all you need to turn any Affect3D animation into a sharable GIF. Enjoy creating mood‑rich loops! If you hit a snag or want ideas for a specific visual style (neon cyber‑punk, moody noir, pastel dreamscape), just let me know and I can walk you through a custom shader or post‑process chain. Happy animating!
// Simple geometry const geometry = new THREE.TorusKnotGeometry(0.8, 0.2, 150, 20); const material = new THREE.MeshStandardMaterial( color: 0xff4e50, metalness: 0.7, roughness: 0.2 ); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); affect3d gif
function render() // Rotate a bit each frame – creates a smooth loop const t = (frameCount / TOTAL_FRAMES) * Math.PI * 2; // 0‑2π mesh.rotation.x = Math.sin(t) * 0.5; mesh.rotation.y = t; | Step | Description | |------|-------------| | |
// Capture the current canvas as a frame every 1/FPS second if (frameCount % Math.round(60 / FPS) === 0) // Grab the raw pixel data const ctx = renderer.domElement.getContext('2d'); const imgData = ctx.getImageData(0, 0, renderer.domElement.width, renderer.domElement.height); gif.addFrame(imgData, delay: 1000 / FPS ); | | Capture | Every 1/30 second we
// Kick‑off render(); </script> </body> </html>
const scene = new THREE.Scene(); scene.background = new THREE.Color(0x0a0a0a);