Context
ORYN Halo is a fictional product launch page for a headset — an “Apple keynote”-style site: cinematic scroll, chained animations, product staging. The technical goal: show what I can deliver for a high-end launch page.
Two main challenges:
- An interactive 3D hero — the headset as a hologram, without a heavy photorealistic asset
- A narrative scroll — each section reveals in sequence, specs animate as you scroll
The 3D hologram
The hero displays the headset as a hologram using a custom GLSL ShaderMaterial in Three.js:
- A beam burst sweeps top to bottom on load (
uReveal: 0 → 1 over 2.4 s), as if the hologram is being assembled before the visitor’s eyes - A Fresnel effect on the edges (the glowing rim typical of sci-fi holograms)
- Animated scanlines in the fragment shader
- A “low battery” glitch: at irregular intervals, the hologram tears in horizontal slices (
uGlitchin the vertex shader) and fades (uFlicker), then recovers - Additive blending — the model sits cleanly against the dark background with no hard outline
The .glb model (Draco-compressed, ~29 KB) is from Poly Pizza (CC-BY licence — attribution in the footer). A generic model is enough: the hologram overrides the lack of detail and gives the page its visual identity.
Hiding the static fallback
A classic WebGL problem: the fallback image shows first, then the 3D appears — an ugly flash. Solution: the holo-ok class is added to <html> synchronously in the <head> as soon as WebGL is available and prefers-reduced-motion is off. The CSS immediately hides the PNG render when holo-ok is present. The visitor never sees both at once.
If WebGL fails, if the .glb fails to load, or if the user has enabled prefers-reduced-motion, restoreFallback() removes the class — the static render returns cleanly.
GSAP scroll
GSAP ScrollTrigger drives the cinematic scroll:
- Hero parallax: the title and product move at different speeds, creating depth
- Cascade reveals: each spec block enters at its own pace as it enters the viewport
- Animated counters: spec figures (battery life, drivers, weight) count up from zero on scroll
Everything degrades gracefully: content is visible statically, prefers-reduced-motion disables animations, and the native IntersectionObserver handles simple reveals.
What the project demonstrates
- Command of GLSL shaders in Three.js (custom vertex + fragment shaders)
- Rigorous progressive degradation: no-JS, no-WebGL, reduced motion
- Ability to deliver a high-end product launch experience without a JS framework
- Careful attention to performance: lightweight Draco model, synchronous detection to avoid flash