1 Concept & brief
Wavelength is a fictional real-time messaging app. The brief called for modern SaaS/consumer-tech glassmorphism: frosted-glass panels, a floating device mockup, an animated gradient-mesh background, and kinetic typography for the hero headline — the kind of front page a funded consumer-tech startup would actually ship.
The name suggested its own motif immediately: sound waves. Rather than treat that as a one-off logo mark, it became the throughline — a five-bar equalizer icon in the nav, an audio-waveform divider between the hero and the feature grid that reacts on scroll, and the kinetic headline reveal itself animates along a sine-wave curve rather than a uniform fade-up. The palette — electric violet through coral to cyan — reads as "signal," which fit a messaging product about instant transmission.
2 Palette, type & layout
Color
| Token | Hex | Use |
|---|---|---|
| Void (background) | #07060d | Base page background |
| Electric violet | #8b5cf6 | Gradient start, primary accent |
| Coral | #ff6b6b | Gradient midpoint |
| Cyan | #22d3ee | Gradient end, status/online indicators |
| Text primary | #f6f5f9 | Headlines, body on dark glass |
| Text muted | #a3a1b8 | Sub-copy |
Every gradient in the UI — buttons, the nav logo, avatars, chart bars — pulls from one CSS custom property, --grad-brand: linear-gradient(115deg, violet 0%, coral 52%, cyan 100%), so the palette stays coherent everywhere instead of drifting per component.
Type
Space Grotesk (600/700) carries every headline and label — a geometric grotesque with enough personality in its lowercase "g" and "y" to feel like a product wordmark rather than a generic system font. Inter handles body copy and UI chrome, chosen for its neutrality at small sizes so it never competes with Space Grotesk for attention. Both load from Google Fonts with display=swap to avoid a blank-text flash.
3 Generated imagery
The brief was explicit that generated imagery should stay abstract — gradient textures, glass sculptures, backdrop art — and that the actual product UI (the chat mockup) should be real HTML/CSS, not an AI-generated image pretending to be a working app. Seven images were generated with Higgsfield's Nano Banana Pro model, then downsampled to WebP with cwebp (all seven together land under 220 KB).
Art direction
The hero mesh background and the repeating mesh texture (the animated mesh-texture.webp behind everything) were each generated from a carefully art-directed brief specifying the lighting, material quality, palette, composition — down to where negative space had to sit for the headline overlay — and mood, iterated until they matched the brand's violet-coral-cyan signal palette. The full prompt library is proprietary and available to clients on request.
Glass orb, glass panels, glass cluster, glass ribbon, glass card
Five more renders — an iridescent glass orb, angled frosted panels, a floating cluster of glass spheres, a twisting liquid-glass ribbon, and a single tilted glass card — each art-directed under the same constraints (no text, no logos, dark void background, product-photography lighting) so they'd composite cleanly as decorative art behind section headers without fighting the real UI for attention. These sit behind the hero (orbit decor), the "How it works" heading (ribbon), the testimonials (cluster), and the pricing section (card) at low opacity with a slow scroll-linked parallax.
4 Glassmorphism
Every "glass" surface — nav, cards, phone frame, buttons — shares one utility class:
.glass {
background: rgba(255,255,255,0.055);
border: 1px solid rgba(255,255,255,0.14);
backdrop-filter: blur(22px) saturate(140%);
-webkit-backdrop-filter: blur(22px) saturate(140%);
box-shadow: 0 1px 0 0 rgba(255,255,255,0.08) inset,
0 30px 60px -30px rgba(0,0,0,0.6);
}
The saturate(140%) alongside the blur is what keeps frosted panels from looking flat and gray — it pulls extra color out of whatever's behind the glass. The inset top highlight plus a soft drop shadow gives each panel a believable edge without a hard outline. Underneath everything sits a fixed, full-viewport .mesh-bg layer: the generated mesh texture as a background-image, animated with a slow 34s CSS keyframe drift (translate + scale + rotate), plus three radial gradients layered on top so the color always feels alive without ever calling attention to itself.
5 Kinetic headline reveal
The hero headline splits into individual characters at runtime and animates in on a sine-wave-shaped stagger — the per-character y-offset and rotation follow Math.sin(i * 0.7) rather than a flat uniform offset, so the entrance itself reads as a wave passing through the words rather than a generic fade-up-and-stagger.
tl.set(allChars, {
opacity: 0,
y: (i) => 46 + Math.sin(i * 0.7) * 22,
rotateZ: (i) => Math.sin(i * 0.9) * 14,
filter: "blur(6px)",
});
tl.to(allChars, {
opacity: 1, y: 0, rotateZ: 0, filter: "blur(0px)",
duration: 0.9, ease: "power4.out",
stagger: { each: 0.022, from: "start" },
});
The gotcha: the second headline line ("speed of thought.") needs a gradient fill. Splitting text into independent inline-block character spans breaks a normal background-clip: text gradient into one mini rainbow per letter, because each span paints its own background independently. The fix: after the characters are laid out in the real DOM, measure each character's offset against the full line's bounding box, then paint the same full-width gradient behind every character, shifted by background-position: -offsetPx 0 — so it reads as one continuous sweep across independently-animating letters instead of a repeated gradient per glyph.
6 The phone mockup — real HTML/CSS, not generated
Per the brief, the chat UI inside the phone frame is hand-built markup: a rounded div frame with a notch, a status bar, a chat header with an "Active now" indicator, and message bubbles positioned with flexbox (align-self: flex-start/flex-end). A GSAP timeline plays the conversation on a loop — bubbles pop in one at a time with a back.out ease, including a three-dot "typing" indicator, then the whole thread fades and replays. Three floating glass chips ("End-to-end encrypted," "Synced in 40ms," "Reaction sent") drift around the frame on independent looping tweens, and the whole phone tilts subtly toward the cursor via gsap.quickTo on rotateX/rotateY for a light parallax feel — capped to pointer-capable devices via matchMedia('(hover: hover)') so touch devices don't get a stuck tilt.
7 Scroll choreography & the waveform motif
Section reveals use GSAP ScrollTrigger with a simple contract: elements start at opacity:0, translateY(28px) and animate in once their top crosses 85–88% down the viewport. The four feature-card mini-demos (an equalizer, an encryption-ring spin, a group-avatar cascade, and a beam that travels between two device icons) each fire independently on their own trigger, so scrolling feels like it's activating things rather than just fading text in.
The signature touch is the waveform strip between the hero and the feature grid: 56 bar elements are generated in JS, given a resting sine-curve height, then driven by a randomized GSAP stagger timeline whenever the strip is in view — a literal audio waveform, tying the animation vocabulary back to the brand name instead of using a generic divider line.
A real bug worth naming: a let waveTl binding was declared after the ScrollTrigger.create() call whose onEnter callback referenced it. ScrollTrigger can fire onEnter synchronously during .create() if the trigger's start point is already behind the current scroll position — which hit the callback before the let statement had executed, throwing a temporal-dead-zone ReferenceError that silently killed every animation defined further down the script. The fix was ordering: declare the function and its closures before the trigger that can call them.
8 Deployment
The whole thing is a static site — plain HTML, one CSS file, one JS file, a folder of WebP images, no build step. It ships to Netlify as a static deploy:
- Drag the
wavelength/folder onto Netlify's deploy target (or connect the repo and set the publish directory towavelength/, no build command needed). - Netlify serves
index.htmlat the root andguide/index.htmlat/guideautomatically — no redirects file required, since both are real files at real paths. - GSAP and Google Fonts load from their CDNs at request time, so there's nothing to bundle.