A small making-of
How to make a screen feel soft.
The concept
The studio makes one tactile object per year, so the site avoids a typical catalogue grid at the top. Instead, the first act is three unlabelled experiments. You can disturb them before you learn what is for sale.
That ordering makes the site about the sensation first: a generous rounded field, heavy Nunito type, objects that look compressible, and copy that cares about the particular pause after a squeeze.
The signature
technique
Each blob is a high-detail three.js icosahedron with a physically based clay material. A tiny rim-light addition in the material shader gives the edges a gentle waxy lift, while soft WebGL shadows anchor the forms to the tray.
Pointer hits use a raycaster. While a blob is held, its target follows the pointer on a flat plane. Every frame, position and three scale axes are pulled toward their targets by damped springs. Velocity stretches the grabbed form; when released, the home target takes over and the blob visibly overshoots before it settles.
function spring(value, velocity, target, stiffness, damping, dt) {
velocity += (target - value) * stiffness * dt;
velocity *= Math.exp(-damping * dt);
return [value + velocity * dt, velocity];
}
blob.velocity.addScaledVector(
destination.clone().sub(blob.group.position),
(grabbed === blob ? 60 : 18) * dt
);
blob.velocity.multiplyScalar(Math.exp(-(grabbed === blob ? 8 : 5.8) * dt));
blob.group.position.addScaledVector(blob.velocity, dt);
const targetScale = blob.base.clone();
if (grabbed === blob) {
targetScale.x += Math.min(blob.velocity.length() * .1, .18);
targetScale.y -= Math.min(blob.velocity.length() * .065, .11);
}Palette + type
The colours deliberately resemble a toy box left in warm daylight. The ink is a softened charcoal rather than pure black; it has enough contrast for the body copy without making the page feel sharp.
#F5B7A8
#A8DCC6
#F7E1A0
#E8E3DC
Fallback: ui-rounded, Arial Rounded MT Bold, sans-serif
Iteration notes
Three review passes, from a reliable tray to a fuller small-world experience.
- Changed the Three.js load to a guarded dynamic import and added a labelled CSS still-life fallback for a failed import, unavailable WebGL, or a lost WebGL context.
- Rendered the complete tray immediately at full scale, then made the render loop start only while the tray is in view and the tab is visible. Rendering resolution remains capped at 2× device pixels.
- Added keyboard operation for the tray: choose blobs with Left/Right, pick one up with Enter or Space, move it with arrow keys, and release it with Escape. Reduced-motion users move and release blobs without an animation loop.
- Raised low-contrast rose and muted text colours, added 44px targets to navigation, footer, and form controls, and made the toast safe on narrow screens.
- Hardened resize and pointer paths, including a ResizeObserver fallback, aspect-aware camera framing, and fresh scene matrices before hit testing. The GPU shader itself could not be visually exercised in this workspace; if it cannot render in a visitor’s browser, the visible still-life fallback explains the limitation instead of leaving a blank tray.
- Weak moment: the archive was a cautious three-up card grid. Recast it as an asymmetric shelf: one tall mint object carries the composition while the butter and pink objects cantilever around it. The oversized, skewed shelf headline and small controlled bleed give the page a stronger middle act.
- Weak moment: the promise section repeated the same anonymous outlined panel three times. Turned those panels into numbered material tests with a deliberate butter → mint → charcoal sequence, different radii, and an alternating step rhythm. Removed the non-functional hover movement rather than animating decoration.
- Weak moment: the clay scene looked soft but touch had too little visible consequence. Added separate damped touch and squeeze springs to the live mesh and its highlight. Hover, pickup, pull speed, and release now alter the local form before the existing position spring brings it home.
- Weak moment: all the memorable touch lived above the fold. Added the return-time test below the archive: a pressable Almost Peach tracks the location of a thumb, compresses with its own damped spring, and keeps a three-imprint notebook beside it.
- Reward and edge state: the third imprint releases a real production detail about the hand-set butter stone. Once the tray is full it explicitly asks for a clear; clearing restores the quiet, empty-notebook state rather than leaving a spent control.
- Finish pass: added matching Open Graph metadata to both pages, retained the CSS still-life while WebGL prepares, and kept the new motion inside the existing reduced-motion contract. The small screen layout collapses the notebook beneath the peach before the page can become cramped.