A rebrand had been on the playing cards for some time. We wished one thing that helped us stand out. Not simply look totally different, however personal what we truly do.
That course of led to a easy idea: magnetic commerce. Nice digital experiences do greater than inform. They draw folks in. That concept formed our new emblem, with a part of the D pulled inward as if by magnetic pressure. The logo turned the place to begin for nearly each design determination that adopted.
As soon as we had that asset rendered as a rotating 3D object, the cursor interplay got here naturally. The emblem ought to behave like its idea. Drag the cursor over it and it pulls. That one element set the tone for the way we’d take into consideration movement throughout the remainder of the location.
The opposite early determination was to let iOS design inform how we deal with playing cards and CTAs. We designed mission playing cards as a deliberate nod in direction of iOS notifications: acquainted, tactile, somewhat sudden in context. The model pointers have been strict. That constraint turned out to be helpful. It stopped selections from drifting and pushed us towards one thing extra thought of.
In whole the mission ran for a number of months. The idea landed comparatively rapidly. Getting the content material proper and the animations correctly refined in improvement: that’s the place the time went.
Figma for design. The model pointers have been tightly outlined, and Figma stored the whole lot systematic (kind, spacing, elements) with out getting in the way in which of iteration.
Webflow for the construct. Our personal web site is constructed on Webflow, and it made sense to make use of it as a proof of what’s attainable on the platform for potential shoppers. Most interactions dwell inside Webflow’s animation system.
GSAP for the elements Webflow’s native interactions couldn’t attain, notably the extra exact cursor-driven behaviours and sequencing.
Customized WebGL for the hero background. That one couldn’t dwell wherever else.
The Hero Background: Distorting a Dwell Floor
The hero background began with an easy concept: use a fullscreen video as a texture and deform it in response to cursor motion. The purpose was to make the background really feel responsive with out turning into distracting.
One factor was vital from the beginning. We didn’t need the impact to behave like a easy ripple centred on the cursor. As an alternative, the distortion ought to proceed within the path of motion, giving the impression that the floor is being pulled reasonably than merely reacting to a hover state.
The Strategy
The impact is applied completely in a fraction shader. As an alternative of layering results over the video, the shader remaps the video’s texture coordinates instantly. The cursor defines the world of affect, whereas the motion path controls how the distortion propagates. A damped sine perform generates the wave that drives the displacement.
The configurable values are outlined as soon as and handed into the renderer:
const SETTINGS = {
radius: 0.41,
amplitude: 0.082,
frequency: 13,
pace: 0.98,
carry: 6,
stagger: 12,
centerPower: 2,
verticalDampPower: 2.2,
motionGain: 220,
speedDecay: 0.86,
};
The geometry consists of a single fullscreen quad:
const vertices = new Float32Array([
-1, -1, 0, 0,
1, -1, 1, 0,
-1, 1, 0, 1,
-1, 1, 0, 1,
1, -1, 1, 0,
1, 1, 1, 1,
]);
The distortion itself occurs within the fragment shader:
float maskFn(vec2 uv) {
vec2 d = uv - uMouseSm;
d.x *= uAspect;
float dist = size(d);
return pow(smoothstep(uRadius, uRadius - uSoft, dist), uCenterPow);
}
float wave = sin(
d.y * uFreq +
dot(d, dir) * uCarry +
uTime * uSpeed +
d.y * uStagger
);
d += dir * (wave * amp * maskFn(uv) * damp);
The result’s an impact that responds to cursor motion with out behaving like a easy hover interplay. The distortion carries momentum within the path of journey earlier than step by step settling.
Movement Course and Persistence
Earlier than values are handed to the shader, the interplay state is smoothed and decayed on the JavaScript facet:
motionTarget *= SETTINGS.speedDecay;
const mappedMotion = Math.min(motionTarget * SETTINGS.motionGain, 1);
movement += (mappedMotion - movement) * SETTINGS.momentum;
dirSm.x += (dirTarget.x - dirSm.x) * SETTINGS.dirSmooth;
dirSm.y += (dirTarget.y - dirSm.y) * SETTINGS.dirSmooth;
This persistence prevents the impact from stopping as quickly because the cursor does. As an alternative, the distortion step by step loses momentum earlier than returning to its resting state, making the interplay really feel extra steady and fewer mechanical.
Visible Restraint
The purpose was so as to add motion to the hero with out competing with the content material. The impact sits behind the typography, including depth whereas protecting the textual content straightforward to learn.
The affect discipline makes use of a tender falloff reasonably than a tough edge, avoiding the look of a highlight across the cursor. Vertical damping additionally limits how far the wave spreads, giving the distortion a extra directional form as a substitute of increasing evenly in each path.
The remainder of the hero is intentionally easy: a black background, a full-bleed video, and the shader utilized on to the feel. Holding the whole lot else minimal helps maintain the concentrate on the interplay itself.
Structure
The construction is pretty easy.
The setup consists of a hero container, a hidden video aspect used as the feel supply, and a WebGL canvas layered above it.
From there, the renderer performs 5 primary duties:
- Put together the video supply and await playback.
- Initialise the WebGL renderer and compile the shaders.
- Observe pointer motion and easy the interplay state.
- Add the present video body to the GPU every body.
- Render a single fullscreen go.
The renderer doesn’t depend on a scene graph, post-processing pipeline, or further geometry. It really works with a single texture, a fullscreen quad, and a pair of shaders, making it easy to combine right into a manufacturing web site.
Body uploads are solely carried out as soon as the video is prepared:
if (video.readyState >= 2) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
}
rafId = requestAnimationFrame(renderFrame);
The implementation retains the render loop light-weight. Interplay values are smoothed over time, the shader solely remaps UV coordinates, the canvas is restricted to the hero part, and rendering doesn’t start till the video is prepared.
The identical strategy could be reused with totally different video sources, distortion profiles, or sections of a web site with out altering the general construction.
Reflections
The ultimate consequence feels a lot nearer to what we wished the location to speak. The technical implementation got here collectively comparatively rapidly. More often than not was spent refining the movement, enhancing the content material, and ensuring each labored collectively.
One of many largest challenges was understanding when to cease including. Small changes to timing, easing, and duplicate typically had extra affect than introducing new results.
Wanting again, the primary factor we’d strategy otherwise is the reliance on cursor interplay. The expertise modifications considerably on contact gadgets, in order that’s one thing we’d think about earlier within the design course of reasonably than adapting later.
Total, the mission bolstered an concept that formed the rebrand from the start: interplay works finest when it helps the idea reasonably than drawing consideration to itself.













