ZEPHYRUS
000
← ARCHIVE
13 July 2026#Astro #AnimeJS #CSS3D #Design #Frontend

Rebuilding This Site, One Ridiculous Animation at a Time

The old portfolio got binned and rebuilt in Astro with anime.js v4, a cel shaded 3D server rack made of pure CSS, a reticle cursor, and physics stickers you can throw around.

The last version of this site did its job, but it never felt like mine. So I binned it and started again, this time with a simple brief, make every section something I would actually want to muck around with.

Here is how it all fits together.

The stack

Astro again, because for a content site it is still the right call. Static output, zero JS by default, and the content collections API keeps the blog posts honest with a Zod schema over the frontmatter. The animation layer is anime.js v4, which turned out to be the star of the whole rebuild. Its onScroll observer with sync: true maps a timeline directly onto scroll position, so scrubbing back and forth replays the choreography in reverse for free.

No Three.js anywhere, which surprised me too. Everything three dimensional on this site is plain CSS, transform-style: preserve-3d, a perspective container, and a fair bit of trigonometry.

The server rack

The homelab section is the bit I am most chuffed with. It is a full 3D server rack built out of divs, six faces plus front rails positioned with rotateY and translateZ like a CSS cube tutorial that got wildly out of hand.

As you scroll, the rack rises from the bottom of the viewport, settles into a display tilt, and one node racks itself out on the Z axis. The services running on the actual homelab, Komodo, Homarr, Grafana, n8n, Prometheus, TimescaleDB, Tailscale, appear as little RAM sticks seated on the exposed board, each with a pointer line and a label. Click a stick and it pops out of its slot with an elastic ease. If a node runs out of slots, the overflow lands on the next node in the rack, and you can swap between them.

The look is cel shaded on purpose. Flat tone bands with hard gradient stops, ink outlines, a warm cream rim light along the top edges, and a hard edged shadow pool underneath. The intake fans on the blank bays spin with repeating-conic-gradient blades, no smooth falloff anywhere, which is exactly what sells the anime feel.

The cursor

There is no native cursor on this site. Instead there is a reticle, four corners and a centre dot, driven by a little spring simulation, velocity plus stiffness and damping, tuned close to critical damping so it settles without ringing.

Hover anything interactive and the reticle springs open to wrap it. The trick is that the target rectangle is re read every animation frame, so when a 3D drawer slides toward the camera, the lock box tracks its projected screen size as it grows. One system, every element, no special cases.

The rest of the toys

A few other bits earn their keep:

  1. The work grid. Project thumbnails are drawn procedurally on canvas, and hovering triggers a halftone reveal, an expanding circle of dots with a dithered edge that swaps the artwork to an alternate state, radiating out from wherever your pointer entered.
  2. The stickers. A matter.js world where die cut stickers fall from above, stack up, and can be grabbed and flung. Completely pointless, entirely necessary.
  3. The archive. The blog list you probably arrived from. Big typographic rows, a green sweep on hover, and a preview card that chases the cursor with a bit of lag and tilts with your horizontal velocity.

Performance notes

Everything animates through transform and opacity only, with will-change on the heavy movers. The scroll driven sections use one timeline each and let the browser compositor do the work. The stickers and canvas loops only run while their sections are actually on screen, courtesy of IntersectionObserver.

The whole thing ships as static HTML with a handful of small script islands, so it still loads like a plain page even though it behaves like a toy box.

What I would do differently

Honestly, not much. Maybe resist the urge to give the fans three different spin speeds. Maybe.

If you want the gory details, the source will land on my GitHub once I have tidied the last few corners. Until then, go throw the stickers around.