Soda

Move the cursor and the can leans toward it while the cherries around it scatter out of the way. Pick a flavor and the can spins, changes its label at the fastest point of the turn, and the cherries come back as blueberries. Grab the can to spin it by hand, or scroll and it travels beside each section's copy.

  • html5
  • css3
  • javascript
  • gsap
Format
Interactive experience
Platform
Desktop + Mobile
Engine
model-viewer + GSAP
Type
Concept

How It Works

1
Every frame

Tilt

One animation loop eases the cursor position into the can's camera, pushes any cherry within 400px of the pointer away from it, and floats the leaves on their own timers.
2
On click

Swap

Choosing a flavor spins the can a full turn under a motion blur, swaps its label texture at the peak of the spin, and pulls every cherry to the center before it bursts back out as the other fruit.
3
On scroll

Travel

Each section moves the can to its own resting spot and turns it exactly once on the way, so it arrives with the label forward. With reduced motion on, scrolling stays native and the section reveals and review marquee hold still.
4
Interactive

Grab

A press on the can itself spins it by hand, and once you let go it coasts before easing back around to the label.

Engineering

  • A can that always lands on its label

    My first scroll version turned the can in step with page height, so each section stopped it wherever that math landed, often on the nutrition panel. I moved the spin onto the same scroll timeline as the can's position, one full turn per section, and every section now settles on a whole rotation. A hand spin coasts first, then eases to the nearest whole turn.
  • Hand spin that works with the frame loop

    The page rewrites the can's camera every frame to follow the cursor, so the viewer's built-in drag input never reached the can. I removed that input and feed the drag in as an offset on top of the loop. The grab checks for a hit by casting a ray from the pointer into the scene, so a click in the empty space beside the can leaves it alone.
  • A model file trimmed at build time

    The cherry model carried two leftover scenes, one holding a mesh the viewer could not map, and it logged an error once for every cherry on the page. My build script keeps only the scene that renders and leaves the compressed geometry untouched, and the page loads with an empty console.

More projects

Get in touch

Working end to end across design, creative, and engineering, where creativity, collaboration, and ownership build toward the company's goals.

jeffkliu@gmail.com