HUD · 2D canvas

Preloader ring

On oryzo.ai

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 ↗

How Lusion does it

A 2D #preloader-canvas: setLineDash circles, arc() sweep; progress comes from the asset loader, no WebGL.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/layers/hud.ts

Controls

HUD · projection

Tracking ring

On oryzo.ai

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 ↗

How Lusion does it

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.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/screen.ts

Controls

HUD · DOM rects

Wordmark rings

On oryzo.ai

The two O’s of ORYZO in the fixed nav are empty; the ring is drawn into them.

oryzo.ai/#hero ↗

How Lusion does it

The O's are layout slots; rings are drawn on the same 2D canvas at the slots' client rects.

How this app does it

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.ts
  • apps/oryzo/src/lib/ui/sections/Nav.svelte
  • apps/oryzo/src/lib/oryzo/text/dom-anchor.ts

Controls

RYZ

Type · SDF

Stagger reveal

On oryzo.ai

Every h1/h2 is invisible in the DOM and redrawn as GPU glyphs; the hero title rises letter by letter.

oryzo.ai/#hero ↗

How Lusion does it

visibility: hidden headings, an MSDF atlas (fonts/msdf/Inter.json|webp, median(), fwidth) and GSAP SplitText to stagger chars, words or lines.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/text/dom-glyphs.ts
  • apps/oryzo/src/lib/oryzo/text/shaders.ts
  • apps/oryzo/src/lib/oryzo/layers/text.ts

Controls

Made for mugs.

Type · SDF

Split from the centre

On oryzo.ai

"Isn't just a coaster" opens from the middle: letters slide outward from each line's centre.

oryzo.ai/#ai ↗

How Lusion does it

SplitText chars staggered by distance from the line centre with an x offset whose sign follows the side.

How this app does it

REVEAL_MODE.split: start order = |aCenter.x| / (uBox.x / 2), the shift flips sign with aCenter.x.

  • apps/oryzo/src/lib/oryzo/text/shaders.ts
  • apps/oryzo/src/lib/oryzo/layers/text.ts

Controls

ISN’T JUSTA COASTER.

Type · SDF

Wipe reveal

On oryzo.ai

Feature titles wipe in from the left at each camera stop and wipe out before the next move.

oryzo.ai/#features ↗

How Lusion does it

A left-to-right SplitText stagger; a CSS clip cannot apply to MSDF quads, so the order is per glyph.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/timeline-data.ts

Controls

Perfectly round,seriously

Type · SDF

Blur in

On oryzo.ai

"Smart flip encryption" starts soft and dim and sharpens while the coaster flips.

oryzo.ai/#encryption ↗

How Lusion does it

Distance-field text blurs for free: widening the edge transition defocuses every glyph at once, no filter pass.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/layers/text.ts

Controls

Smart flipencryption

Type · SDF

Specular sweep

On oryzo.ai

A bright slanted band crosses "sustainability" once as the section scrolls.

oryzo.ai/#sustainability ↗

How Lusion does it

A colour ramp keyed on the glyph's position in the text box, driven by the smooth-scroll value.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/timeline-data.ts

Controls

sustainability

Type · depth

World-space occlusion

On oryzo.ai

"it's wearable" is a real object: the coaster passes behind the letters, then in front.

oryzo.ai/#wearable ↗

How Lusion does it

Two text paths: screen-locked text drawn after post, and world text at z = 0 in the lit scene with depth write.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/layers/text.ts
  • apps/oryzo/src/lib/oryzo/post.ts

Controls

wearable

Type · SDF

Dimmed copy

On oryzo.ai

Body paragraphs sit dark and light up word by word as they reach mid-screen.

oryzo.ai/#features ↗

How Lusion does it

SplitText words with a scroll-scrubbed colour tween per word.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/text/shaders.ts

Controls

Cork is bark.

Post

Bokeh depth of field

On oryzo.ai

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 ↗

How Lusion does it

A circle-of-confusion pass from the depth buffer after SMAA, then a colour LUT, lens dirt, vignette and dither.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/scene.ts

Controls

3D

Cutting mat and lighting

On oryzo.ai

A green cutting mat with a grid on a wooden desk, one warm key light, a soft shadow under the coaster.

oryzo.ai/#features ↗

How Lusion does it

PBR materials (brdf.png, transmission/ior/thickness for glass parts), an environment map, textured mat.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/objects/grid-shader.ts
  • apps/oryzo/src/lib/oryzo/materials/wood-texture.ts

Controls

3D

Cork macro

On oryzo.ai

The grip section cuts to a full-bleed close-up of the cork cell structure, drifting slowly.

oryzo.ai/#grip ↗

How Lusion does it

A wide-fov camera parked centimetres from the rim with close focus and a strong CoC; the cork is a photographic texture set.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/objects/coaster.ts
  • apps/oryzo/src/lib/oryzo/screen.ts

Controls

3D

Flip and contact shadow

On oryzo.ai

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 ↗

How Lusion does it

GSAP tweens on position and rotation scrubbed by scroll; a blurred decal under the object scales with height.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/objects/coaster.ts
  • apps/oryzo/src/lib/oryzo/layers/coaster.ts

Controls

3D

Product variants

On oryzo.ai

Three pills swap the coaster between Oryzo, Pro and Pro Max; the mesh changes, material and shadow stay.

oryzo.ai/#product ↗

How Lusion does it

Prepared meshes toggled from the DOM through app state.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/objects/coaster.ts
  • apps/oryzo/src/lib/oryzo/layers/coaster.ts
  • apps/oryzo/src/lib/ui/sections/Picker.svelte

Controls

Variant

3D · splines

Ribbons

On oryzo.ai

Orange strips loop through the wearable area and slide along their paths as you scroll.

oryzo.ai/#wearable ↗

How Lusion does it

Strip meshes along splines with the wearable/condom/* textures; the UV offset is scrubbed by scroll.

How this app does it

Closed Catmull-Rom loops sampled into a table; update(phase, reveal) rewrites the strip vertices in place.

  • apps/oryzo/src/lib/oryzo/objects/ribbon.ts
  • apps/oryzo/src/lib/oryzo/layers/ribbons.ts

Controls

3D · instancing

Bean burst

On oryzo.ai

At the footer hundreds of coffee beans burst out of the coaster and swirl in smoke-like curls.

oryzo.ai/#footer ↗

How Lusion does it

GPGPU particles: position and velocity in ping-pong FBOs advected by curl noise, drawn with InstancedBufferGeometry.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/layers/beans.ts
  • apps/oryzo/src/lib/oryzo/objects/bean-geometry.ts

Controls

Post · film

Grain and vignette

On oryzo.ai

The whole frame carries a fine moving grain, darkened corners and a hint of lens dirt; dark gradients never band.

oryzo.ai/#hero ↗

How Lusion does it

Final pass: LUT grade, lens_dirt.jpg blend, vignette, dither.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/config.ts

Controls

DOM · CSS

Glass panel and glow frame

On oryzo.ai

Feature cards are frosted panels over the scene; hovering the AI section lights a pulsing glow around the viewport.

oryzo.ai/#ai ↗

How Lusion does it

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.

How this app does it

.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.css
  • apps/oryzo/src/lib/ui/sections/Ai.svelte
  • apps/oryzo/src/lib/ui/sections/Features.svelte

Controls

Glow frame

Engine

Scroll damping

On oryzo.ai

Wheel scrolling is smoothed: the page keeps gliding after the wheel stops and the camera never snaps.

oryzo.ai/#hero ↗

How Lusion does it

A custom wheel-driven smooth scroll (normalizeWheel, lerp, velocity) on a 43 181 px #site-content with a fixed hero.

How this app does it

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.ts
  • apps/oryzo/src/lib/oryzo/scene.ts
  • apps/oryzo/src/routes/+page.svelte

Controls

scroll   0
damped   0.0
velocity 0 px/s