Hinweis: Dieser Inhalt wurde automatisch übersetzt. Feedback senden

Dithered SVG Particles

Jedes SVG wird zu einem Feld aus über 10k interaktiven Partikeln. Hover drückt sie auseinander, Klick feuert eine Schockwelle. 2D Canvas, kein WebGL.

2026-04

Lokalisierte Projektversion mit bewahrtem Stack, Links, Code und zentralen technischen Details.

An experiment in porting Emil Kowalski's dithered-canvas technique to work with any SVG. The mark loads, rasterizes, and becomes a cloud of responsive dots — no shaders, no libraries, just typed arrays and bucketed 2D canvas draws.

How it works

  1. The SVG loads as an <img> and rasterizes into a 256×256 offscreen canvas.
  2. Every pixel becomes a particle; RGB is quantized to 4-bit bins.
  3. Dots with the same bin collapse into one color bucket (~20–80 per SVG).
  4. Draw pass: one fillStyle per bucket, batched fillRects. 10k+ particles at 60fps.
  5. Hover pushes with cubic falloff; click fires an expanding shockwave; requestAnimationFrame stops at rest.

The craft details

  • Pre-allocated Float32Array / Int32Array for all per-dot state — no per-frame allocation, no GC pressure.
  • Exponential smoothing at 12%/frame (disp += (target - disp) × 0.12) gives spring-adjacent feel for a fraction of the cost of real physics.
  • Epsilon snap (Math.abs(disp) < 0.01 → 0) is what lets the animation loop actually end — without it, dots drift in sub-pixel floats forever and burn CPU indefinitely.
  • Pointer filtering — only pointerType === 'mouse' triggers the hover loop. Touch devices skip the expensive per-frame repulsion but still get click shockwaves.
  • data-ready attribute flips after the first paint, triggering a CSS fade-in. Avoids the flash of blank canvas on mount.

The whole component is ~300 lines. The technique scales: a four-shape brand mark, an illustrated portrait, or an icon — if you can render it as an SVG, the particle field picks up the colors automatically.