HUD · 2D canvas
Before the hero, a dashed ring with four square anchors and bezier handles sits at the centre; an orange arc fills clockwise while assets load.
oryzo.ai/#hero ↗A 2D #preloader-canvas: setLineDash circles, arc() sweep; progress comes from the asset loader, no WebGL.
Same ring on the fixed #hud canvas: drawRing() draws guides, two dashed circles, arc and handles; the HUD layer feeds it frame.loading; here hud.preload forces preload mode after ready.
apps/oryzo/src/lib/oryzo/hud/ring.tsapps/oryzo/src/lib/oryzo/layers/hud.tsHUD · projection
Once loaded the ring glides onto the coaster and follows it, staying a circle while the coaster tilts; dotted guides cross the viewport at its tangents.
oryzo.ai/#hero ↗The ring is 2D: the coaster's world position is projected every frame and the radius scaled by view depth, so the overlay never picks up perspective.
trackTarget() projects sample.coaster.position; pixelRadius() turns the 180 px radius at that depth into CSS px; preload → track blends over 0.8 s.
apps/oryzo/src/lib/oryzo/layers/hud.tsapps/oryzo/src/lib/oryzo/screen.tsHUD · DOM rects
The two O’s of ORYZO in the fixed nav are empty; the ring is drawn into them.
oryzo.ai/#hero ↗The O's are layout slots; rings are drawn on the same 2D canvas at the slots' client rects.
Every [data-ring] span is measured in measure(); hud.rings fades rings at 0.46 × width, dots and anchors shrunk for the 11 px nav O’s.
apps/oryzo/src/lib/oryzo/layers/hud.tsapps/oryzo/src/lib/ui/sections/Nav.svelteapps/oryzo/src/lib/oryzo/text/dom-anchor.tsRYZ
Type · SDF
Every h1/h2 is invisible in the DOM and redrawn as GPU glyphs; the hero title rises letter by letter.
oryzo.ai/#hero ↗visibility: hidden headings, an MSDF atlas (fonts/msdf/Inter.json|webp, median(), fwidth) and GSAP SplitText to stagger chars, words or lines.
The atlas is rasterised at runtime with tiny-sdf from the same woff2 the DOM uses; the browser lays the hidden headline out and measureGlyphs() reads one client rect per character; uReveal plus a per-glyph start (aIndex / uCount, width uRevealSpread) staggers in the vertex shader.
apps/oryzo/src/lib/oryzo/text/atlas.tsapps/oryzo/src/lib/oryzo/text/dom-glyphs.tsapps/oryzo/src/lib/oryzo/text/shaders.tsapps/oryzo/src/lib/oryzo/layers/text.tsType · SDF
"Isn't just a coaster" opens from the middle: letters slide outward from each line's centre.
oryzo.ai/#ai ↗SplitText chars staggered by distance from the line centre with an x offset whose sign follows the side.
REVEAL_MODE.split: start order = |aCenter.x| / (uBox.x / 2), the shift flips sign with aCenter.x.
apps/oryzo/src/lib/oryzo/text/shaders.tsapps/oryzo/src/lib/oryzo/layers/text.tsType · SDF
Feature titles wipe in from the left at each camera stop and wipe out before the next move.
oryzo.ai/#features ↗A left-to-right SplitText stagger; a CSS clip cannot apply to MSDF quads, so the order is per glyph.
REVEAL_MODE.wipe: order = glyph x across the box; the timeline runs text.f1.reveal 0 → 1 → 0 around each stop.
apps/oryzo/src/lib/oryzo/text/shaders.tsapps/oryzo/src/lib/oryzo/timeline-data.tsType · SDF
"Smart flip encryption" starts soft and dim and sharpens while the coaster flips.
oryzo.ai/#encryption ↗Distance-field text blurs for free: widening the edge transition defocuses every glyph at once, no filter pass.
uBlur widens soft in the coverage step, capped at half the SDF range; opacity rides 0.45 → 1 with it.
apps/oryzo/src/lib/oryzo/text/shaders.tsapps/oryzo/src/lib/oryzo/layers/text.tsType · SDF
A bright slanted band crosses "sustainability" once as the section scrolls.
oryzo.ai/#sustainability ↗A colour ramp keyed on the glyph's position in the text box, driven by the smooth-scroll value.
uSweep runs −0.4 → 1.4 across the box; a gaussian band with slant uSweepSlant mixes uSweepColor into the fill.
apps/oryzo/src/lib/oryzo/text/shaders.tsapps/oryzo/src/lib/oryzo/timeline-data.tsType · depth
"it's wearable" is a real object: the coaster passes behind the letters, then in front.
oryzo.ai/#wearable ↗Two text paths: screen-locked text drawn after post, and world text at z = 0 in the lit scene with depth write.
data-anchor="world" puts the mesh in scene instead of screenGroup; the fragment shader discards below uDepthCutoff so glyphs write real depth; text.<id>.z moves the plane.
apps/oryzo/src/lib/oryzo/text/material.tsapps/oryzo/src/lib/oryzo/layers/text.tsapps/oryzo/src/lib/oryzo/post.tsType · SDF
Body paragraphs sit dark and light up word by word as they reach mid-screen.
oryzo.ai/#features ↗SplitText words with a scroll-scrubbed colour tween per word.
Only the resting state is ported: data-fx="dim" sets #2a221d at 0.5 × text.<id>.opacity; a per-word scrub would use aWord / uWords from the same vertex data.
apps/oryzo/src/lib/oryzo/layers/text.tsapps/oryzo/src/lib/oryzo/text/shaders.tsPost
The desk falls out of focus toward the far edge while the coaster stays crisp; the grip section is almost all bokeh.
oryzo.ai/#hero ↗A circle-of-confusion pass from the depth buffer after SMAA, then a colour LUT, lens dirt, vignette and dither.
Hand-rolled chain: scene → SMAA → 41-tap disc bokeh reading the real depth texture → grain, vignette, dither, tone map; focus is the sharp view depth, aperture the blur growth per unit, clamped by maxblur.
apps/oryzo/src/lib/oryzo/post.tsapps/oryzo/src/lib/oryzo/scene.ts3D
A green cutting mat with a grid on a wooden desk, one warm key light, a soft shadow under the coaster.
oryzo.ai/#features ↗PBR materials (brdf.png, transmission/ior/thickness for glass parts), an environment map, textured mat.
Procedural: patchGridMaterial() draws the grid in the fragment shader, the wood is a canvas texture, the key light casts a 2048² PCF shadow, RoomEnvironment gives the speculars.
apps/oryzo/src/lib/oryzo/objects/desk.tsapps/oryzo/src/lib/oryzo/objects/grid-shader.tsapps/oryzo/src/lib/oryzo/materials/wood-texture.ts3D
The grip section cuts to a full-bleed close-up of the cork cell structure, drifting slowly.
oryzo.ai/#grip ↗A wide-fov camera parked centimetres from the rim with close focus and a strong CoC; the cork is a photographic texture set.
screenDistance 400 widens the fov while keeping 1 unit = 1 px at 400 units; the cork is procedural (createCorkTextures: map, roughness, bump) on a MeshPhysicalMaterial with sheen.
apps/oryzo/src/lib/oryzo/materials/cork-texture.tsapps/oryzo/src/lib/oryzo/objects/coaster.tsapps/oryzo/src/lib/oryzo/screen.ts3D
The coaster lifts off the mat, turns over and lands on its other face; its shadow spreads and fades as it rises.
oryzo.ai/#encryption ↗GSAP tweens on position and rotation scrubbed by scroll; a blurred decal under the object scales with height.
Keyframes in timeline-data.ts: onMat(0, −40, h) lifts along the mat normal, rotation MAT_TILT + π; setLift(h) scales the contact disc by 2.3R(1 + 0.5h/R) and fades it by 1/(1 + 2h/R).
apps/oryzo/src/lib/oryzo/timeline-data.tsapps/oryzo/src/lib/oryzo/objects/coaster.tsapps/oryzo/src/lib/oryzo/layers/coaster.ts3D
Three pills swap the coaster between Oryzo, Pro and Pro Max; the mesh changes, material and shadow stay.
oryzo.ai/#product ↗Prepared meshes toggled from the DOM through app state.
The lathe profile is rebuilt: buildCoasterProfile() → LatheGeometry for single, pro (seam) and proMax (Ø ×1.22); the DOM dispatches oryzo:variant, coaster.variant does the same from the timeline.
apps/oryzo/src/lib/oryzo/objects/coaster-profile.tsapps/oryzo/src/lib/oryzo/objects/coaster.tsapps/oryzo/src/lib/oryzo/layers/coaster.tsapps/oryzo/src/lib/ui/sections/Picker.svelte3D · splines
Orange strips loop through the wearable area and slide along their paths as you scroll.
oryzo.ai/#wearable ↗Strip meshes along splines with the wearable/condom/* textures; the UV offset is scrubbed by scroll.
Closed Catmull-Rom loops sampled into a table; update(phase, reveal) rewrites the strip vertices in place.
apps/oryzo/src/lib/oryzo/objects/ribbon.tsapps/oryzo/src/lib/oryzo/layers/ribbons.ts3D · instancing
At the footer hundreds of coffee beans burst out of the coaster and swirl in smoke-like curls.
oryzo.ai/#footer ↗GPGPU particles: position and velocity in ping-pong FBOs advected by curl noise, drawn with InstancedBufferGeometry.
CPU, 600 instances: beanAt(seed, t) is a pure function of burst time (eased radial flight + curl of three value-noise fields + t² gravity) written into an InstancedMesh when beans.burst changes; skipped on coarse pointers.
apps/oryzo/src/lib/oryzo/objects/beans-field.tsapps/oryzo/src/lib/oryzo/layers/beans.tsapps/oryzo/src/lib/oryzo/objects/bean-geometry.tsPost · film
The whole frame carries a fine moving grain, darkened corners and a hint of lens dirt; dark gradients never band.
oryzo.ai/#hero ↗Final pass: LUT grade, lens_dirt.jpg blend, vignette, dither.
FINAL_FRAG: hash-noise grain re-seeded per frame, smoothstep(0.35, 0.95, r) vignette, ACES tone map, ±0.5/255 dither; no LUT, no dirt.
apps/oryzo/src/lib/oryzo/post.tsapps/oryzo/src/lib/oryzo/config.tsDOM · CSS
Feature cards are frosted panels over the scene; hovering the AI section lights a pulsing glow around the viewport.
oryzo.ai/#ai ↗backdrop-filter panels; the glow is stacked box-shadows driven by a --pulse custom property; dashed rules are repeating-linear-gradients; type scales with --screen-unit.
.glass = backdrop-filter: blur(24px) saturate(1.2) over the GL canvas (this card); .glow-frame is a fixed conic-gradient ring cut out with mask-composite: exclude, toggled by html.is-glowing; GL glyphs under a backdrop-filter blur with the scene, so headlines never sit inside a panel.
apps/oryzo/src/app.cssapps/oryzo/src/lib/ui/sections/Ai.svelteapps/oryzo/src/lib/ui/sections/Features.svelteEngine
Wheel scrolling is smoothed: the page keeps gliding after the wheel stops and the camera never snaps.
oryzo.ai/#hero ↗A custom wheel-driven smooth scroll (normalizeWheel, lerp, velocity) on a 43 181 px #site-content with a fixed hero.
Native scroll; the DOM is never transformed, only the GL side follows window.scrollY through stepDamped() (tau 0.12 s), so copy stays where the browser put it; here the coaster rides the damped value, the dashed DOM circle the raw one.
apps/oryzo/src/lib/oryzo/scroll.tsapps/oryzo/src/lib/oryzo/scene.tsapps/oryzo/src/routes/+page.sveltescroll 0 damped 0.0 velocity 0 px/s