3D Model
Format: glTF 2.0 binary (.glb) with Draco mesh compression and basic JPEG color texture.
Source asset: 670 KB compressed (originally 28 MB · 967k tris · simplified to ~77k tris via meshoptimizer, then Draco-compressed).
The model is a single mesh (one material, one texture). The K display, body and LED edges share the same UVs and texture image.
Loading: use GLTFLoader + DRACOLoader (decoder from Google CDN https://www.gstatic.com/draco/v1/decoders/).
Three.js Setup
Version pin: three@0.160.0 via ESM importmap.
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
Renderer: WebGLRenderer({ alpha: true, antialias: true }), outputColorSpace = SRGBColorSpace, pixel ratio capped at 2.
Lights: Ambient + Directional Key (lavender) + Directional Fill (cyan) + Rim + Hemisphere — modulated each frame for subtle pulse.
Camera & Hero Pose
Camera: PerspectiveCamera(33, aspect, 0.01, 100) at (0, 0.2, 3.2), looking at origin.
Hero rotation (target rest pose):
boxGroup.rotation.y = -0.42 // ~ -24°
boxGroup.rotation.x = 0.26 // ~ +15°
boxGroup.rotation.z = 0 // wobbles ±0.04
Idle motion: float + small wobble in compound sin waves (±8° max). User drag overrides; after 1.5s of no interaction the box gently returns to hero pose.
LED Pulse Animation
Every ~5 seconds the model briefly increases material.emissiveIntensity (from 0.45 baseline to ~2.0 peak) over ~700ms, creating a "breathing" LED flash effect on the K display and edges.
v5 (planned): extract emissive mask from the JPEG texture isolating only cyan/LED pixels, apply as emissiveMap modulated by camera-facing angle so LEDs glow strongest when facing the viewer.
Background Chart
SVG-based dot-matrix grid (30 cols × 16 rows on A3). Each column shows the pool value as a stack of filled dots from the bottom, with a glowing top "peak" dot and a thin vertical stem (candle-style).
Colors: filled #B79CFF, peak #FFFFFF, empty outline #5CE5FF at 30% opacity.
Edge fade: container has a CSS mask-image: radial-gradient(...) → no rectangular frame, the chart breathes into the background.
Re-renders on each pool tick (1.5–3s interval).
Typography
Family: Encode Sans from Google Fonts. Weights loaded: 400, 500, 600, 700, 800, 900.
Display / headline / counter use 900 (Black). Subline uses 500. UI labels use 700–900 with uppercase + letter-spacing.
<link href="https://fonts.googleapis.com/css2?family=Encode+Sans:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
Color Tokens
primary#6E2DFF
primary-bright#8650FF
primary-light#B79CFF
primary-glow#D6C5FF
primary-dark#2D0E5C
neon-cyan#5CE5FF
green#10E090
alert (live)#FF4D6A
bg#0A0518
Mobile / Responsive
Toggle body.is-mobile at viewport width < 980px. Layout flips to single column with 3D box on top, text below.
- Headline scales via
clamp(26px, 8vw, 36px)
- CTA button: smaller padding + font
- Pixel ratio capped at 2 to save GPU on mobile devices
- Animation auto-pauses on
visibilitychange when tab is hidden
- Ticker bar pinned at bottom, hugs viewport edges on mobile
Integration Steps
-
1
Copy the
model.glb (670 KB) to your asset bucket / CDN. Reference its URL in the loader instead of the base64 blob used in this preview.
-
2
Wire the pool value to your live pricing feed. Replace the local
tickPool() stub with a WebSocket or polling endpoint that pushes { value, history }.
-
3
Connect the
Claim your share → CTA to your deposit / signup flow. Ticker copy is static here — replace with localized strings from your CMS.
-
4
Bind the Encode Sans font through your site's global font setup. The CSS color tokens map 1:1 to the Kickback design system primary palette.
-
5
Run Lighthouse with mobile throttling — target LCP < 2.5s. The 3D model is the largest payload; lazy-init the Three.js scene below the fold if the hero is paginated.
Performance Notes
- Model: 670 KB Draco-compressed (decode happens via WASM, ~150ms on mid-range mobile)
- WebGL renderer: single canvas per hero variant, pixel ratio capped at 2
- Animation loop auto-pauses on
document.hidden
- Background chart re-renders only on pool tick (not every frame)
- Inactive variants stop their render loop when the picker switches
- Font is preconnected to
fonts.googleapis.com for faster first paint