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:

  1. An interactive 3D hero — the headset as a hologram, without a heavy photorealistic asset
  2. 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 (uGlitch in 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