Affect3d Gif ((free)) -
// ------------------------------------------------- // 3️⃣ Animation loop (simple rotation) // ------------------------------------------------- const FPS = 30; // GIF frame‑rate const DURATION = 2; // seconds const TOTAL_FRAMES = FPS * DURATION;
// ------------------------------------------------- // 2️⃣ Post‑processing (the “affect” part) // ------------------------------------------------- const composer = new EffectComposer(renderer); composer.addPass(new RenderPass(scene, camera)); affect3d gif
let frameCount = 0; const gif = new GIF( workers: 2, quality: 10, workerScript: 'https://cdn.jsdelivr.net/npm/gif.js/dist/gif.worker.js' ); | | Capture | Every 1/30 second we
| 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! 4️⃣ Common Gotchas & How to Fix Them
// ------------------------------------------------- // 4️⃣ Encode & download // ------------------------------------------------- function finalizeGif() gif.on('finished', blob => const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'affect3d-loop.gif'; a.click(); URL.revokeObjectURL(url); ); gif.render();
// Lights const dir = new THREE.DirectionalLight(0xffffff, 1); dir.position.set(5, 5, 5); scene.add(dir); scene.add(new THREE.AmbientLight(0x404040, 0.5));