Building a portfolio nobody asked for
A portfolio is supposed to be a list. Job, dates, bullet points, repeat. Mine is a badge hanging on a lanyard that you can grab, throw and spin, and scrolling rewinds it through every office I have worked in.
Nobody asked for this. No recruiter has ever said "I would hire him, but the physics on his job history is not convincing". That is the point ๐. Your own site is the one codebase where the requirements are yours, the deadline is imaginary, and you can spend a week on a problem purely because it is interesting.
The idea is not mine. I saw a draggable lanyard badge on another developer's
portfolio, could not stop poking at it, and wanted to know how it held together.
So I rebuilt it from scratch rather than lifting the implementation: no physics
engine, no 3D library, just a verlet chain in an SVG and a DOM element with
transform-style: preserve-3d. Credit for the concept goes to whoever built the
original. Everything broken in mine is my own work ๐
.
What I did not expect is how much of that week went on problems that had nothing to do with physics.
The strap is the easy part
Fourteen points, gravity, and a constraint that pulls neighbouring points back toward a rest length. A verlet chain is maybe forty lines and it works on the first evening.
Then you attach a card to it, and the trouble starts.
A bug is not always a tuning problem
The card kept slamming when you let go of it. The obvious response is to tune: lower the gravity, raise the damping, bleed velocity on release. I did all three and it still slammed ๐.
The real cause was that dragging pinned the end of the rope to the cursor.
A position constraint does not care about force. The solver stretched the rope to reach the pointer and stored that as tension, then release handed all of it back at once.
A gentle pull and a violent one produced exactly the same lurch, which was the clue I ignored for far too long. Replacing the pin with a damped spring fixed it in six lines, because a spring can only store what your pull is actually worth.
The model decides what is possible
Later the card started shaking violently when dragged straight up. The hang angle
came from atan2 over the last few rope points, and lifting the card inverts
that vector, so I clamped it: if the strap is slack, snap to the sideways limit
and pick a side from the sign of the horizontal offset.
Which works, until you drag straight up, where that offset hovers around zero and the sign flips every frame. The card was being told to point sixty degrees left, then sixty degrees right, sixty times a second ๐ต.
The fix was not a smaller clamp. It was noticing that a slack strap has no meaningful direction at all, and fading the whole target toward upright as the rope goes slack. Continuous, so there is no sign left to flip.
Ropes do not push
The same drag also made the chain jitter, and that one was embarrassing. My constraint corrected compression as well as extension:
if (d <= seg) continue; // a rope pulls, it never pushes
One line. A rope resists being stretched and does nothing at all when slack, and I had been modelling it as a strut.
Three of my worst bugs were the same mistake wearing different clothes. Each time I had modelled something as more rigid than it really is:
| I built | It should have been |
|---|---|
| a pin | a spring |
| a sign flip | a fade |
| a strut | a rope |
Tuning constants is what you do when the model is right. When tuning stops converging, that is usually the model trying to tell you something.
Why bother
Because a week spent on a lanyard taught me more about damping, rasterisation and device pixel grids than a week of tutorials would have. Every one of those bugs has an analogue in ordinary work: state that is too rigid, a boolean where a gradient belongs, a constraint applied in both directions when the domain only has one.
Also it is fun to throw. That counts for something ๐.