Auralis
aadmin2026-11-05 发布Site模板10 阅读

Auralis

类型:Site模板 · 收录:2026-11-05 01:13:00 · 源码:会员可见

[hidecontent type="vip1"]
# Recreate this site as a single HTML file: Auralis

You are an expert creative front-end developer. Produce a **single self-contained `index.html`**
that reproduces the project below **exactly** — same layout, visuals, motion, and interaction.
Pure HTML/CSS/JS in one file: no build step, no framework, no bundler. Use ES modules with a CDN
importmap for Three.js 0.180.0 and Lenis 1.3.19. Hardcode every value given here as a fixed
constant. Rebuild each block described below as a section of the one file.

## What it is

A one-page scroll film for **Auralis**, a fictional AI-native design studio. The whole page is one
pinned WebGL scene — a silver-grey foggy void where each act is a **wireframe containment lattice
floating in the haze** (no ground, no podium, no fixtures) — and scroll drives a single continuous
camera shot through four acts:

1. **The glyph.** When the preloader lets go, ~32 000 tiny white spheres **fly in from around the
   camera** and assemble into a volumetric "X" over ~1.8 s, each carrying a pink comet trail
   mid-flight; the lattice then **weaves itself closed** around the nearly-finished figure,
   segment by segment. The X floats (slow yaw rock + bob), tilts a few degrees toward the cursor,
   repels around it, then bursts into thin glowing violet ribbon streamers strung along the
   flight corridor as the camera dollies forward with a gentle **~15° mid-flight bank** (out and
   back — never a full roll) while the lattice un-weaves.
2. **The hand.** A second lattice weaves in from the fog carrying a particle hand (a real point
   cloud, ~50 000 beads) that scans in bottom-up with a pink band while **pirouetting a full
   eased 360°** about its own axis.
3. **The wave.** The hand's own particles crumble through several meandering bright pink streams
   (a vortex of glowing spines) and settle onto a huge rippling grid curtain tilted into the fog
   at the lower left, while the camera cranes up off the hand. Sparse pink motes rise and
   dissolve over the curtain.
4. **The tree.** A tilted screen-space wipe sweeps bottom-up, revealing a separately-framed final
   tableau — a ~150 000-particle tree that materialises bottom-up through an angled scan, its
   lattice weaving closed around it at half-height, pink fireflies climbing around the crown, the
   tree camera drifting on a slow sub-unit orbit — with a centred ENVISION heading and a glass
   contact bar.

Every resting figure carries **GPU idle life**: a violet band drifts through the beads (a tint,
never a glow) plus a gentle whole-form sway; every built lattice **turns forever** (~80 s per
revolution) and sparkles with **pink impulses** — a rising accent ring plus sparse per-segment
flashes. Over the canvas: three blended light-flare PNGs that parallax the cursor, bank with the
camera, and **stage their own entrance** on the preloader hand-off; film grain; a glass header;
and per-act UI overlays whose type reveals letter-by-letter through a blur-and-lift spring engine,
paced to land with the particle fly-in.

## Page shell & libraries

- **Importmap:**

  ```html
  <script type="importmap">
  { "imports": {
    "three": "https://unpkg.com/three@0.180.0/build/three.module.js",
    "three/addons/": "https://unpkg.com/three@0.180.0/examples/jsm/",
    "lenis": "https://unpkg.com/lenis@1.3.19/dist/lenis.mjs"
  } }
  </script>
  ```

  From `three/addons/`: `postprocessing/EffectComposer.js`, `postprocessing/RenderPass.js`,
  `postprocessing/ShaderPass.js`, `postprocessing/OutputPass.js`, `postprocessing/Pass.js`
  (for `Pass` + `FullScreenQuad`), `shaders/VignetteShader.js`.

- **Fonts** — the display face is **Google Sans Flex (9pt optical)**, three static weights,
  self-hosted; load with `@font-face` from these URLs and use
  `font-family: "Google Sans Flex", system-ui, sans-serif` everywhere:
  - 200: https://storage.getlayers.ai/assets/auralis-7725b062e5/GoogleSansFlex_9pt-ExtraLight.ttf
  - 300: https://storage.getlayers.ai/assets/auralis-7725b062e5/GoogleSansFlex_9pt-Light.ttf
  - 400: https://storage.getlayers.ai/assets/auralis-7725b062e5/GoogleSansFlex_9pt-Regular.ttf

- **Scroll model** — the page is `1180vh` of scroll driven by a spacer `<div>`; the scene canvas
  and every overlay live in a `position: fixed; inset: 0` stage above it. Scroll progress is
  `scrollY / (scrollHeight − innerHeight)`, clamped to [0, 1], **memoised once per frame** and
  shared by every rAF consumer. Smooth the wheel with Lenis (`new Lenis({ smoothWheel: true })`,
  its own rAF loop) — native scroll stays the source of truth.

- **Adaptive rem grid** — rem-based layout with viewport-scaled root font-size, exactly:

  ```css
  html { font-size: 16px; }
  @media (max-width: 1920px) { html { font-size: 0.833333vw; } } /* 16*100/1920 */
  @media (max-width: 1440px) { html { font-size: 1.111111vw; } } /* 16*100/1440 */
  @media (max-width: 1024px) { html { font-size: 1.5625vw;   } } /* 16*100/1024 */
  @media (max-width: 640px)  { html { font-size: 4.444444vw; } } /* 16*100/360  */
  ```

  The `md` breakpoint used by every layout rule below is **768px**.

- **Colour tokens** (the DOM palette — glass white-on-scene):

  ```css
  :root {
    --scene-backdrop: #8d959e;             /* behind the canvas until it paints */
    --overlay-ink: #ffffff;                /* nav, hero words, labels, buttons  */
    --overlay-scrim: rgba(0,0,0,0.1);      /* header bar fill                   */
    --overlay-glass: rgba(255,255,255,0.1);/* tag pills + ghost button fill     */
    --overlay-line: rgba(255,255,255,0.2); /* header underline + hairlines      */
    --overlay-edge: rgba(255,255,255,0.4); /* ghost borders + brand rules       */
    --overlay-strong: rgba(255,255,255,0.8);/* stat left-border accents         */
    --overlay-solid: #000000;              /* filled button / preloader / menu  */
    --overlay-accent: #6fff85;             /* blinking "live" square on the CTA */
    --grain-opacity: 0.13;
    --blur-overlay-header: 28px; --blur-overlay-glass: 6px; --blur-overlay-form: 16px;
  }
  ```

- **Film grain** — a fixed full-viewport layer over the canvas and flares, under the UI
  (z-index 5), `mix-blend-mode: overlay`, opacity `var(--grain-opacity)`, tiled 160px SVG noise:

  ```
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E")
  ```

- **Stage stacking** (inside the fixed stage, which carries `isolation: isolate` and
  `overflow: hidden`): canvas (auto) → light flares (auto) → grain (z 5) → act overlays (z 10) →
  header (z 20) → mobile-menu panel (z 50) / its close button (z 60) → preloader (z 70).

## The device tiers

Read ONCE at boot; everything derives from it:

- `mobile` = `innerWidth < 768` **or** `matchMedia("(hover: none) and (pointer: coarse)")`;
  `tablet` = `< 1180`; else `desktop`.
- Particle count scale: desktop 1 / tablet 0.6 / mobile 0.34 (glyph min 8000, hand density min 4,
  tree density min 1, dust min 200, wave motes min 50, tree motes min 40).
- DPR clamp: mobile `min(dpr, 0.85)`; tablet `clamp(dpr, 0.75, 1.25)`; desktop
  `clamp(dpr, 0.75, 1.5)`.
- Shadows OFF on mobile; shadow map **2048² on desktop and tablet** (the beads are too small to
  resolve a finer texel).
- Frame budget: mobile 33.3ms (30fps), tablet 22.2ms (45fps), **desktop 14ms** — a deliberate
  ~60fps cap so 120/144 Hz displays don't run the whole pipeline at double rate (14, not 16.7,
  so a 60 Hz display's ticks always clear the budget).
- Pointer listeners are **not attached at all** on mobile.
- Bloom strength ×0.5 on mobile, and ×`min(1, max(0.5, innerHeight/1080))` everywhere.

## The WebGL scene

Renderer: **`antialias: false` on every tier** (everything visible renders through the composer's
render targets, which carry no MSAA — the flag would cost memory for zero smoothing), `alpha:
false`, `stencil: false`, `powerPreference: "high-performance"` (default on mobile);
`ACESFilmicToneMapping`, exposure 1; `PCFSoftShadowMap` (enabled off-mobile). Camera
`PerspectiveCamera(32, aspect, 0.1, 600)`. `scene.fog = new Fog('#868e96', 25, 130)`.

**There is no ground, no podium and no light fixture** — each act is a containment lattice
floating in the gradient haze.

### Backdrop (verbatim shader)

A `SphereGeometry(400, 32, 32)` with `BackSide`, `depthWrite: false`, `fog: false`; uniforms
`topColor '#aab2ba'`, `bottomColor '#868e96'`, `gradientScale 110`, `gradientOffset 0`:

```glsl
// vertex
varying vec3 vWorldPosition;
void main() {
  vec4 worldPosition = modelMatrix * vec4(position, 1.0);
  vWorldPosition = worldPosition.xyz;
  gl_Position = projectionMatrix * viewMatrix * worldPosition;
}
// fragment
uniform vec3 topColor;
uniform vec3 bottomColor;
uniform float gradientScale;
uniform float gradientOffset;
varying vec3 vWorldPosition;
void main() {
  float h = clamp(vWorldPosition.y / gradientScale + gradientOffset, 0.0, 1.0);
  gl_FragColor = vec4(mix(bottomColor, topColor, h), 1.0);
}
```

Also PMREM a 100-radius copy of this backdrop into `scene.environment` (the particle material
carries a faint environment term).

### The containment lattices (hero / hand / tree — self-weaving)

`WireframeGeometry(new CylinderGeometry(7.6, 7.6, 18, 56, 20, true))` as `LineSegments`,
`LineBasicMaterial({ color '#ffffff', transparent, opacity 0.32, depthWrite: false })`, centre
y 10.5. The **hero act's lattice is ×1.2** (radius and height, base kept planted at the same
floor height). The hand's copy sits at (0, hand.center.y+1, −110) scaled (0.72, 0.92, 0.72); the
tree's at (0, tree.center.y+1, −175).

Wireframe output is non-indexed vertex *pairs*: give both vertices of each segment one shared
random (`aSeed`, mulberry32 seed **20260728**) and an end param (`aEnd` 0 → 1). Patch the
material via `onBeforeCompile` with uniforms `uFieldHeight`, `uBuild` (build state 0 → 1),
`uTime`, `uAccent` (the shared pink `#b552ff`):

```glsl
// vertex additions
varying float vFieldFade; varying float vLineT; varying float vBuildD;
varying float vFieldT; varying float vSeed;
uniform float uFieldHeight; uniform float uBuild;
attribute float aSeed; attribute float aEnd;
// after begin_vertex:
float fieldT = position.y / uFieldHeight + 0.5;
vFieldFade = pow(clamp(sin(fieldT * 3.14159265), 0.0, 1.0), 1.5);   // fades at BOTH ends
float bThr = 0.8 * (0.45 * fieldT + 0.55 * aSeed);                  // bottom-biased stagger
vBuildD = clamp((uBuild - bThr) / 0.2, 0.0, 1.0);
vLineT = aEnd; vFieldT = fieldT; vSeed = aSeed;
```

```glsl
// fragment — replaces `vec4 diffuseColor = vec4( diffuse, opacity );`
float bDraw = vBuildD * 1.25;
float bA = (1.0 - smoothstep(bDraw - 0.25, bDraw, vLineT)) * smoothstep(0.0, 0.06, vBuildD);
float bPulse = vBuildD * (1.0 - vBuildD) * 4.0;
float impRing = pow(0.5 + 0.5 * sin(vFieldT * 4.0 - uTime * 0.9), 24.0);
float impFlash = pow(0.5 + 0.5 * sin(uTime * 0.8 + vSeed * 97.0), 36.0);
float imp = min(1.0, impRing + impFlash) * vBuildD;
float accentMix = max(bPulse * 0.85, imp * 0.9);
vec4 diffuseColor = vec4( mix(diffuse, uAccent, accentMix),
  opacity * vFieldFade * bA * (1.0 + bPulse * 1.5 + imp * 2.0) );
```

Each segment *draws itself* from one end as `uBuild` passes its threshold, flashing toward the
accent mid-draw; a built lattice carries **pink impulses** (a rising accent ring + sparse
per-segment glints). Every lattice also spins forever: `lines.rotation.y = time · 0.08`.

### Rising accent motes (two systems — verbatim shaders)

Additive pink sparks that climb and dissolve, fully GPU-driven (one `uTime` write per frame).
`Points` + `ShaderMaterial` (transparent, `depthWrite: false`, `AdditiveBlending`,
`frustumCulled: false`), per-mote `aSeed` vec4 = (phase, speed cycles/s, sway amp, size jitter):

```glsl
// vertex
uniform float uTime;
uniform float uRise;
uniform float uSize;
uniform float uScale;
attribute vec4 aSeed;
varying float vFade;
void main() {
  float t = fract(aSeed.x + uTime * aSeed.y);
  vec3 p = position;
  p.y += t * uRise;
  p.x += sin(uTime * 0.5 + aSeed.x * 41.0) * aSeed.z;
  p.z += cos(uTime * 0.42 + aSeed.x * 57.0) * aSeed.z;
  vFade = smoothstep(0.0, 0.18, t) * (1.0 - smoothstep(0.6, 0.95, t));
  vec4 mv = modelViewMatrix * vec4(p, 1.0);
  gl_Position = projectionMatrix * mv;
  gl_PointSize = uSize * aSeed.w * (uScale / -mv.z);
}
// fragment
uniform vec3 uColor;
uniform float uOpacity;
varying float vFade;
void main() {
  float a = smoothstep(0.5, 0.1, length(gl_PointCoord - 0.5));
  if (a < 0.02) discard;
  gl_FragColor = vec4(uColor, a * vFade * uOpacity);
}
```

Seeds: phase `random()`, speed `speed[0] + random()·(speed[1]−speed[0])`, sway
`sway·(0.5+random())`, size jitter `0.6 + random()·0.8`; base positions scattered over the
emitter box floor. **Wave motes** (over the curtain): count 130, centre (−8, 18, −116), spreadX
34, spreadZ 10, rise 22, sway 0.9, speed [0.02, 0.05], size 0.42, opacity 0.5, colour `#b552ff`,
seed 5150 — hidden until `seqP > 0.35` (they'd read as stray specks over the X). **Tree motes**
(fireflies, inside the tree view group): count 110, centre (0, 3, −175), spreadX 11, spreadZ 9,
rise 20, sway 1.2, speed [0.025, 0.06], size 0.5, opacity 0.65, seed 6060.

### Dust (three fields: origin, at the hand anchor, at the tree anchor)

700 points (× tier scale) in a 38-unit half-extent box, height 34, **size 0.3, opacity 0.72**,
seeded RNG, custom `ShaderMaterial` (transparent, `depthWrite: false`):

```glsl
// vertex
uniform float uSize;
uniform float uScale;
varying float vDepth;
void main() {
  vec4 mv = modelViewMatrix * vec4(position, 1.0);
  vDepth = -mv.z;
  gl_Position = projectionMatrix * mv;
  gl_PointSize = uSize * (uScale / -mv.z);
}
// fragment
uniform vec3 uColor;
uniform float uOpacity;
uniform float uDispersion;   // 0 — plain round motes
uniform vec3 uFogColor;      // #868e96
uniform float uFogNear;      // 25
uniform float uFogFar;       // 130
varying float vDepth;
void main() {
  vec2 uv = gl_PointCoord - 0.5;
  vec2 off = vec2(uDispersion, -uDispersion);
  float aR = smoothstep(0.5, 0.08, length(uv + off));
  float aG = smoothstep(0.5, 0.08, length(uv));
  float aB = smoothstep(0.5, 0.08, length(uv - off));
  float a = max(aR, max(aG, aB));
  if (a < 0.02) discard;
  vec3 col = uColor * vec3(aR, aG, aB);
  float fog = smoothstep(uFogNear, uFogFar, vDepth);
  col = mix(col, uFogColor, fog);
  gl_FragColor = vec4(col, a * uOpacity * (1.0 - fog * 0.6));
}
```

`uScale` = drawingBufferHeight × 0.5. Per frame each mote floats up from a static home
(`y = (homeY + time·rise) % 34`, rise 0.03–0.12) and sways
(`x = homeX + sin(time·0.08 + phase)·amp`, `z = homeZ + cos(time·0.065 + phase)·amp`,
amp 0.3–1.2).

### Seeded RNG + curl noise (exact)

mulberry32: `a = seed>>>0; a=(a+0x6d2b79f5)>>>0; t=a; t=Math.imul(t^(t>>>15),1|t);
t=(t+Math.imul(t^(t>>>7),61|t))^t; return ((t^(t>>>14))>>>0)/4294967296`.

Curl of a value-noise vector potential (finite differences, eps 0.4). Hash:
`h = x*374761393 + y*668265263 + z*2147483647 + seed*1274126177; h=(h^(h>>13))*1274126177;
h=h^(h>>16); return (h>>>0)/4294967295` (seeds 11 / 53 / 97 with offsets (+31.4,+17.2,+47.1) and
(−19.7,−8.3,−27.9)); trilinear value noise with quintic smoothing; curl = the usual
∂P/∂y−∂P/∂z cross-terms via central differences.

### The particle clouds (glyph / hand / tree — one shared builder)

Each cloud is an `InstancedMesh` of **`SphereGeometry(0.5, 8, 6)`** (96 tris — the beads render
at a few px) + `MeshStandardMaterial({ color '#ffffff', roughness 0.98, metalness 0,
envMapIntensity 0.08 })`, `castShadow` + `receiveShadow` (glyph and resting hand only),
`frustumCulled: false`. Per instance: jittered position, uniform scale on the matrix diagonal, an
`instanceColor` carrying a baked half-Lambert form shade (`wrap = (0.5 + 0.5·(n·L))^1.7`,
`shade = 0.82 + 0.18·wrap ±0.02`), and a per-instance `aGlow` vec3 attribute added as pure
emissive. ~4% of beads (`bigFraction 0.04`) are 2× "hero" beads with an always-on white emissive
`bigGlow 5` — only they (and cursor-touched / scattering beads) clear the bloom threshold.

Patch `MeshStandardMaterial` via `onBeforeCompile` (give the two variants different
`customProgramCacheKey`s — `"particle-cloud"` and `"particle-cloud-relax"` — or the tree silently
reuses the glyph's program and its hover dies). Vertex additions after `#include <common>`:

```glsl
attribute vec3 aGlow;
varying vec3 vGlow;
uniform vec3 uPointer;
uniform float uPointerR;
uniform float uPointerS;
uniform vec3 uPointerTint;
uniform float uScan;
uniform float uScanMinY;
uniform float uScanMaxY;
uniform vec3 uScanTint;
uniform float uTime;
uniform vec3 uFlowTint;
uniform float uFlowAmp;
// relax variant only:
attribute vec2 aRelaxUv;
uniform sampler2D uRelaxMap;
```

`vGlow = aGlow;` after `#include <begin_vertex>`, and replace `#include <project_vertex>` with:

```glsl
vec4 instCenter = vec4( 0.0, 0.0, 0.0, 1.0 );
#ifdef USE_INSTANCING
  instCenter = instanceMatrix * instCenter;
#endif
vec3 wCenter = ( modelMatrix * instCenter ).xyz;
float sH = clamp( ( wCenter.y - uScanMinY ) / max( 1e-4, uScanMaxY - uScanMinY ), 0.0, 1.0 );
float sLine = uScan * 1.05;
float sDd = sLine - sH;
float sRev = clamp( sDd / 0.05, 0.0, 1.0 );
float sActive = 1.0 - step( 0.9999, uScan );
float sGlow = sActive * max( 0.0, 1.0 - sDd / 0.14 ) * step( 0.0, sDd );
vec3 scanned = transformed * sRev;
vec4 mvPosition = vec4( scanned, 1.0 );
#ifdef USE_INSTANCING
  mvPosition = instanceMatrix * mvPosition;
#endif
vec4 wPos = modelMatrix * mvPosition;
/* GPU idle life — a whole-form sway plus a tinted band drifting through the cloud: */
float lifePhase = wCenter.x * 0.23 + wCenter.y * 0.31 + wCenter.z * 0.17;
wPos.xyz += uFlowAmp * vec3( sin( uTime * 0.6 + lifePhase ), sin( uTime * 0.5 + lifePhase * 1.7 ), cos( uTime * 0.7 + lifePhase * 1.3 ) );
float lifeBand = 0.5 + 0.5 * sin( wCenter.y * 0.24 + wCenter.x * 0.1 - uTime * 0.5 );
vGlow += uFlowTint * ( lifeBand * lifeBand * lifeBand );
/* relax variant: */
vec4 rel = texture2D( uRelaxMap, aRelaxUv );
wPos.xyz += rel.xyz;
vGlow = max( vGlow, uPointerTint * rel.w );
/* analytic variant instead: */
vec3 toC = wCenter - uPointer;
float pd = length( toC );
float pInf = max( 0.0, 1.0 - pd / uPointerR );
wPos.xyz += normalize( toC + vec3( 1e-4 ) ) * ( pInf * pInf * uPointerS );
vGlow = max( vGlow, uPointerTint * ( pInf * pInf ) * step( 0.0001, uPointerS ) );
/* both: */
vGlow = max( vGlow, uScanTint * sGlow );
mvPosition = viewMatrix * wPos;
gl_Position = projectionMatrix * mvPosition;
```

Fragment: declare `varying vec3 vGlow;` after `<common>` and append
`totalEmissiveRadiance += vGlow;` after `#include <emissivemap_fragment>`.

Per-cloud life values (`uFlowTint = #b552ff × flow`, `uFlowAmp = sway`): glyph flow 0.55 / sway
0.05; hand 0.45 / 0.04; tree 0.8 / 0.14 (the baked tree leans on it hardest — it is its only
per-bead motion). Flow strengths sit far below the bloom threshold: a tint, never a glow.

**Glyph geometry** (seed 20260720, then builder seed +1): rejection-sample 32 000 points inside
the union of two bars crossing at ±45° (`DIAGONAL = √½`; **bar length 16.8, width 3.1, depth
3.4** — the ×1.2 hero; in-bar test on rotated coords), keep ~4.5% of near-miss samples inside a
**1.1 spill** shell, jitter ±0.25 (**edgeNoise 0.5**), rotate the whole X by π/7 about Y, centre
(0, 11, 0), **particleSize 0.12** ± 0.55. Per-point form normal = smooth rounded-box normal
(face normals weighted by `frac^3`, normalised, Y-rotated) — that is what the baked shade lights
against key direction (−14, 26, 34).

**Hand** (seed 90210): fetch the point cloud
`https://storage.getlayers.ai/assets/auralis-7725b062e5/hand-points.bin` — 4173 points, three
uint16 per point, decoded `offset + (u16/65535)·scale` with offset
(−0.5973206758499146, −0.9999998807907104, −0.6382458806037903) and scale
(1.194641351699829, 1.9999998807907104, 1.2764917612075806), × scale 7. Upsample ×12 (first copy
exact, the rest jittered `±0.5 · particleSize·(1.5 + 12·0.3)`), rotate −π/6 about Y, centre
(0, 8, −110), light (−18, 42, 16), particleSize 0.1, sizeJitter 0.4. Its `scatterDest` is a
**wave-curtain grid**: 336 columns × 150 rows, spacing 0.41, centre (−8, 33, −120), tilted 0.45
and yawed 0.6 (grid cell i → `ox=(gx−half)·s`, `dy=(gy−half)·s`, `oy=dy·cosT`, `oz=−dy·sinT`,
world = centre + (ox·cosY + oz·sinY, oy, −ox·sinY + oz·cosY)), one particle per cell in order.

**Tree** (seed 71717): fetch
`https://storage.getlayers.ai/assets/auralis-7725b062e5/tree-points.bin` — 50 000 points, same
u16 format, offset (−0.8996697664260864, −1.0000001192092896, −0.5329897403717041), scale
(1.7993395328521729, 2.0, 1.0659794807434082), × scale 8, upsample ×3, rotation 0, centre
(0, 9, −175), light (−14, 26, 34), particleSize 0.08, sizeJitter 0.4, `castShadow/receiveShadow
false`. The tree opts into the **relax** shader variant with an `aRelaxUv` attribute (texel
centres of the smallest square texture that fits the count).

### The per-frame cloud update (the heart of the piece)

The glyph and the hand are re-simulated on the CPU every frame; every value **eases** toward its
target: `current += (target − current) · ease`, with ease = `0.22` (relax) while
touched/scattering/waving, `0.035` (returnRelax) otherwise, floored at `0.05` (idle), raised to
`max(ease, 0.6·flightEased)` during the flight, `max(ease, 0.3)` while scanning, and
`max(ease, 0.4)` during the intro assembly. Per bead:

- **Intro assembly** (the opening move): each bead launches from
  `startPos + flowVector·spread 11` — the camera's opening pose (0, 12, 50) scattered by its own
  random flow vector — with a per-bead staggered start (`stagger·0.6` of the window), and
  travels to its rest spot on a smootherstep. Waiting beads are hidden (scale snaps to 0); in
  flight a bead rides at `0.55 + 0.45·progress` size and carries a **pink comet trail**
  `sin(π·progress)·4.2` through the shared scan-tint path (so the streams bloom); it lands at
  full size with the trail burnt out.
- **Idle shimmer** (always): `target += sin(time·0.18 + phase)·0.16` per axis (phases
  `stagger·2π`, ×1.3/×1.7 and ×0.8 variants on Y/Z).
- **Cursor repulsion** (idle acts, gated): raycast the cursor onto the camera-facing plane
  through the act's focus; for beads within radius 5: `influence = (1 − d/5)·gate`, displacement
  `influence²·6` along the outward radial, violet heat `#b579ff × 3.6 × influence²` on `aGlow`.
- **Scatter → flight** (the X burst): each bead belongs to one of 3 corridor ribbons
  (`ribbon = i % 3`). Per ribbon: `hash = sin(ribbon·127.1)·43758.5453`, `rnd = fract(hash)`,
  `phase = ribbon·2.3999632 + rnd·2π`, `rBase = 14·(0.55 + 0.9·rnd)`. Corridor param `p` =
  the bead's stagger (0 tip → 1 far), evaluated as:
  `angP = phase + p·1.4 + sin(p·2.2·2π + time·0.5 + phase)·0.8`;
  `radP = rBase·(1 + p·0.5)·(1 + 0.3·sin(p·2.2·2π·0.7 + time·0.65 + phase))`;
  position `x = cos(angP)·radP + tube offsets`, `y = 14 + (sin(angP)·radP + tube)·0.5`,
  `z = −6 + p·(−114)`; tube offset = a fixed per-bead point in the unit disc × thickness 0.2.
  Blend rest → corridor by smoothstep of the flight amount; bloom
  `min(1, flightEased·1.6)·(1 − radial·0.5)` in violet `#b552ff × 5.5`.
  **Exit**: `hp = exit·1.2`, `pShift = t + hp`; past the tip (`pShift > 1`) fly out along a
  smooth arc — heading turns from −Z to the ribbon's side via smoothstep
  (`th = −π/2 + side·(π/2)·uc`), distance `u·60`, lift `uc·14`; soft floor
  `if (fy < 16) fy = 1 + 2.5·ln(1 + e^((fy−1)/2.5))`; global fade over exit 0.72→1.
- **Hand → wave** (the vortex morph): per bead staggered progress
  `ph = ((i·20261) % 1000)/1000 · 0.26`, `wl = clamp((wave − ph)/(1 − 0.26))`, smootherstep
  `we`. Travel rest → curtain cell + ripple
  `rip = (5·sin(cx·0.16 + t·0.5) + 5·sin(cy·0.2 + t·0.4))·we`. Vortex pull
  `vpk = min(1, sin(π·we)·1.3)·1` toward one of 4 snaking spines: base spread ±24 across X
  (×0.3 in Z), snake `sin(hY·1.6·2π + strand·1.7 + t·0.4)·9` (Z variant ×0.5, phase +1.7,
  freq ×0.6), radius `2·rj^6` (dense core), swirl angle `ha·2π + hY·5 + t·0.8`, height
  `minY + hY·46`; hashes `hY = ((i·2654435761)%1000)/1000`, `rj = ((i·40503)%997)/997`,
  `ha = ((i·12289)%1009)/1009`. Bloom `vpk·2.2·(0.12 + 0.88·(1−rj))` in the shared pink.
  Size ×`1 + (0.9−1)·we`; edge fade: shrink beads in the outer 28% of the recovered grid-local
  extent (smoothstep); albedo lerps to pure white over the **second half** of `we`; a white
  emissive lift `2·smoothstep((we−0.5)/0.5)` floors the settled curtain's brightness (below the
  bloom threshold). **Sparse hand**: a fixed 55% slice (`((i·2246822519)%1000)/1000 < 0.55`) is
  held at zero scale until the wave, growing in over the morph's first 35% — the hand reads airy,
  the curtain keeps every bead. Held/unrevealed beads snap to scale 0, never eased.
- **Scan-in** (the hand's reveal, CPU side): reveal height `h = (y − minY − (x − cx)·tilt)/span`,
  `dd = scan − h`; scale 0 below, ramp over 0.05, glow band `(1 − dd/0.14)·scanGlow 6` tinted
  the shared pink. The tree's scan runs on the **GPU** via `uScan`/`uScanTint` (scanGlow 4.2) —
  never re-simulate 150k beads.
- Albedo fades toward black as a bead blooms (`keep = 1 − min(1, bloom)·0.85`) so the pink reads
  saturated.

The **tree** is updated exactly once at load (with `ease = 1`) and then only rendered. Its cursor
hover runs entirely on the GPU: a ping-pong half-float state texture (one texel per bead —
`xyz` displacement, `w` heat) advanced one fullscreen pass per frame with the same law
(`inf = d < 5 ? (1 − d/5)·gate : 0`, target `toC·(inf²·6/max(d,1e-4))`, ease
`inf > 0 ? 0.22 : 0.035` via `mix(prev, target, ease)`), sampled in the vertex shader through
`aRelaxUv`. Rest positions live in a full-float `DataTexture`; targets are `HalfFloatType`,
`NearestFilter`, cleared to zero on first step.

### Whole-figure float + cursor tilt (mesh-level, every frame)

Each cloud's `InstancedMesh` gets a mesh-level rotation + the compensating translation that keeps
the pivot at the figure's own centre (`rotation.set(pitch, yaw, 0)`, then
`position = centre − centre.applyEuler(rotation) + (0, bob, 0)`). The cursor contributes an eased
tilt: low-pass the pointer NDC with `k = 1 − 0.94^(dt·60)`, then `yaw += ndc.x·0.07`,
`pitch −= ndc.y·0.04` (zero on touch). Per figure:

- **Glyph**: `yaw = (sin(t·0.45)·0.12 + cursorYaw)·heroHold`, `pitch = cursorPitch·heroHold`,
  `bob = sin(t·0.3)·0.3·heroHold`, where `heroHold = 1 − smoother(range(seqP, 0, 0.08))` — the
  rock settles level as the burst begins.
- **Hand**: `yaw = handSpin + (sin(t·0.27+1)·0.045 + cursorYaw)·handHold`, `bob =
  sin(t·0.19+2)·0.12·handHold`, where **`handSpin = smoother(range(seqP, 0.42, 0.57))·2π`** — a
  full eased pirouette during the scan-in (2π ≡ 0, so the wave morph still sees an unrotated
  mesh) — and `handHold` ramps in over `seqP` 0.44→0.52 and out before the morph
  (`transformRange[0]−0.06 → transformRange[0]`).
- **Tree**: `yaw = sin(t·0.24+3)·0.055 + cursorYaw`, `bob = sin(t·0.17+1)·0.18` — floats through
  its whole act.

### Post chain

`EffectComposer`: RenderPass → WipePass (disabled outside the transition) → custom BloomPass →
VignetteShader (offset 1, darkness 1.25) → OutputPass.

**BloomPass** (selective additive bloom — threshold 3.5, strength 1.5, radius 1.9; half-res
ping-pong, two separable blur iterations, `radius·(i+1)`):

```glsl
// bright pass
uniform sampler2D tDiffuse; uniform float threshold; uniform float knee; varying vec2 vUv;
void main() {
  vec4 c = texture2D(tDiffuse, vUv);
  float l = dot(c.rgb, vec3(0.2126, 0.7152, 0.0722));
  float s = smoothstep(threshold, threshold + knee, l);
  gl_FragColor = vec4(c.rgb * s, 1.0);
}
// blur (9-tap separable Gaussian; direction carries the radius in px)
vec2 px = direction / resolution;
vec4 sum = texture2D(tDiffuse, vUv) * 0.227027;
sum += texture2D(tDiffuse, vUv + px * 1.3846) * 0.316216;
sum += texture2D(tDiffuse, vUv - px * 1.3846) * 0.316216;
sum += texture2D(tDiffuse, vUv + px * 3.2308) * 0.070270;
sum += texture2D(tDiffuse, vUv - px * 3.2308) * 0.070270;
gl_FragColor = sum;
// combine
gl_FragColor = vec4(base.rgb + bloom * strength, base.a);
```

(knee 0.05.) Skip the pass entirely when effective strength < 0.001.

**WipePass** (the wave→tree film wipe; both inputs are HalfFloat render targets):

```glsl
uniform sampler2D tWave;
uniform sampler2D tTree;
uniform float uWipe;
uniform float uTilt;      // 0.4
uniform float uAA;        // 0.0016
uniform float uWaveShift;
uniform float uTreeShift;
varying vec2 vUv;
void main() {
  float lineC = vUv.y + (vUv.x - 0.5) * uTilt;
  float m = 1.0 - smoothstep(uWipe - uAA, uWipe + uAA, lineC);
  vec3 waveC = texture2D(tWave, vUv + vec2(0.0, uWaveShift)).rgb;
  vec3 treeC = texture2D(tTree, vUv + vec2(0.0, uTreeShift)).rgb;
  gl_FragColor = vec4(mix(waveC, treeC, m), 1.0);
}
```

### Lighting

HemisphereLight('#e6ecf2', '#8a929a', **0.3**) + AmbientLight(0xffffff, **0.12**) + key
DirectionalLight('#ffffff', 2.6) at (−14, 26, 34) targeting (0, 11, 0), casting shadows (bias
−0.0003, normalBias 0.08, near 1, far 110, frustum ±14 / top 20 / bottom −14) — the glyph's
volume comes from real particle-on-particle self-shadowing. **Gate the shadow depth pass on the
hero act**: `renderer.shadowMap.autoUpdate = glyphActive` (with `needsUpdate = true` on
re-entry) — past the burst the hand and tree sit beyond the shadow camera's far plane and the
pass would rasterise an empty map every frame. Desktop/tablet add a rim
DirectionalLight('#e6ecf2', 1.1) at (14, 18, −30).

### The scroll choreography (all boundaries exact)

Real progress `p` ∈ [0,1] over 1180vh. The X→hand→wave act runs in remapped
`sp = clamp01(p / 0.72)`; `treeStart = 0.72`.

- Camera: holds until `sp` 0.02, then dollies (0,12,50)→(0,12,−60) with targets
  (0,10,0)→(0,8,−110) over [0.02, 0.56] (smootherstep), **banking `0.26·sin(π·ep)` radians**
  about the view axis — out to ~15° mid-flight and back upright by `arrive` (Rodrigues rotation
  of the up vector; guard the near-vertical case). From `sp` 0.56→1 it lifts level to
  (0,15,−92) looking at (0,48,−134). For `p ≥ 0.72` it **holds** there.
- Intro (wall-clock, from the preloader hand-off): the X assembles over **1.8 s**; the hero
  lattice waits until the assembly is **75%** done, then weaves in over **1.4 s**
  (`uBuild = heroWeave·(1 − range(seqP, 0.02, 0.26))` — the same value un-weaves it on scroll).
- Glyph: cursor gate fades out over [0, 0.02]; flight assembles over `sp` [0.03, 0.2]; exit
  (ribbons whip away) over [0.24, 0.5]; the glyph mesh stops rendering past `sp` 0.54.
- Hand: visible past `sp` 0.4; its lattice weaves over `sp` [0.3, 0.47]; scan-in (+ the 360°
  pirouette) over `sp` [0.42, 0.57]; cursor gate ramps in over [0.56, 0.60], drops before the
  wave starts; wave morph over `sp` [0.66, 0.99] (the wave gate re-enables the cursor once
  `waveAmount` ≥ 0.85, focused on the wave centre).
- Wipe: over real `p` [0.74, 0.95], `uWipe = −0.3 + w·1.6`, tilt 0.4. During
  `p ∈ [0.72, 0.95)` render the live wave view (hand cloud at wave 1) into rtWave from the main
  camera and the tree view into rtTree from the **drifting tree camera** (base (0,12,−125) →
  (0,9,−175), position + `(sin(t·0.1)·1.1, sin(t·0.13+1.3)·0.5, 0)`, re-`lookAt` each frame);
  parallax `uWaveShift = 0.07·w`, `uTreeShift = −0.045·(1−w)`. Past 0.95 render the tree view
  directly (same drifting camera; keep the hidden wave updating so scrolling back never shows a
  stale frame).
- Tree scan: over real `p` [0.88, 0.99] via `uScan`, tint = pink × 4.2; the tree's lattice
  weaves from the **scan's midpoint** (`p` 0.935) to the scan's end — the wipe uncovers empty
  haze, the tree grows, the cage closes around it.

Scene clock: `elapsed += min(0.05, dt)` in seconds — drives drift, ripple, dust, life and idle.

## The light flares (DOM, over the canvas)

Three layers inside the stage, `pointer-events: none`, each a `<div>` with a background PNG:

1. **Rays** — https://storage.getlayers.ai/assets/auralis-7725b062e5/light_1.png; a centred
   **square of side 130vmax** (so rotating never exposes an edge), `background-size: cover`,
   `mix-blend-mode: overlay`. It **banks with the camera**:
   `deg = −(0.26·sin(π·smootherstep(range(sp, 0.02, 0.56))))·180/π`. Cursor parallax ±32px
   (spring-smoothed, tension 120 friction 28).
2. **Glint** — light_2.png anchored bottom-left: `left: −16vw; bottom: −18vh; width: 78vw;
   height: 88vh`, `transform-origin: 84.6% 22.7%`, `mix-blend-mode: plus-lighter`, masked by
   `radial-gradient(115% 115% at 22% 82%, #000 30%, transparent 68%)`. Same bank; parallax
   ∓46px (opposite phase).
3. **Bokeh** — light_3.png, `inset: −10vmax`, `mix-blend-mode: overlay`, parallax ±34px.

**Entrance** (on the preloader hand-off, springs tension 55 friction 26): the rays swing into
place — opacity 0→1, a residual −9° rotation and 1.06× scale settling onto the camera-locked
bank — starting 150ms after the hand-off; the glint rises out of its corner (+50px x/y → 0)
starting at 500ms.

Opacity choreography: rays+glint own the X/hand acts, fade **out** over
`sp ∈ [0.56, 0.98]` (smootherstep) as bokeh fades **in** (the crane-up crossfade); past
`p = 0.72` rays+glint return to full but are **masked to the tree side of the wipe line**, and
bokeh is masked to the wave side, so the flares hand over exactly with the scene wipe
(reconstruct the tilted line from `uWipe`; mask = `linear-gradient(angleDeg, #000 e%,
transparent e%)`, the glint intersecting its feather; the bokeh's cut inverted; carry mask +
opacity in the same per-frame write so they never disagree). Bokeh is gone once `p ≥ 0.95`.
Parallax is disabled (drift → 0) while the wipe masks are active. All flares hold still on
touch devices.

## Header (persistent, z 20)

Fixed top bar, height 3.25rem (2.625rem ≥ md), `background: var(--overlay-scrim)`,
`backdrop-filter: blur(28px)`, bottom hairline `var(--overlay-line)`, padding-x 1.25rem
(1.875rem ≥ md), font 1rem. Desktop: left nav **Projects / Studio / Process**, centred brand
**Auralis** flanked by two 5rem hairline gradient rules (transparent → `var(--overlay-edge)`),
right nav **Journal / About** + underlined **Say Hello** (gap 2.5rem, links fade to 70% on
hover). Below 768px: brand + a burger (two 1.25rem × 1px rules, gap 0.375rem) that opens a
full-screen black (`--overlay-solid`) panel — links stacked centred, 1.5rem extra-light
uppercase, `tracking 0.2em`, staggered in ~55ms apart with a spring, the burger's rules crossing
into an X (±45°, y ∓3px); Escape or a link closes it; page scroll locks while open.

## The four act overlays (z 10, `pointer-events: none`, cross-faded by scroll)

Each overlay is `position: absolute; inset: 0`, shown only in its window via
smoothstep opacity ramps of real progress (`visibility: hidden` under 0.01 opacity):

- **Hero (the X):** on from p 0 (`appear [0,0]`), fades out over [0.05, 0.14].
- **Hand:** in over [0.34, 0.41], out over [0.47, 0.53].
- **Wave:** in over [0.6, 0.67]; **never opacity-fades out** — it is clipped by the wipe (below).
- **Tree:** in over [0.93, 0.98], stays to the end.

**Text reveal engine.** All copy animates through one inline spring engine (mass 1, tension 88,
friction 30 — overdamped, no overshoot): split headings **per letter** (stagger 22ms) and body
copy **per word** (stagger 45ms); hidden state `translateY(90%)` (words 80%) + `opacity 0` +
`blur(12px)`, shown state `0% / 1 / blur(0)`. Non-text blocks (pills, buttons, cards, stat
blocks, the contact bar) rise 30px and fade on the same spring, staggered 80ms per item —
**no blur on blocks**. Each act has an "active window" of real progress — hero [0, 0.04], hand
[0.35, 0.46], wave [0.61, 0.955], tree [0.94, 1] — and elements play **in** when the window
becomes active and **out (the same animation reversed, stagger reversed)** when it leaves, in
both scroll directions. Nothing plays until the preloader hands off.

- **Hero overlay:** two 4rem (2.5rem mobile) extra-light uppercase display words at the exact
  vertical centre — **IMAGINE** pinned left (1.875rem), **MANIFEST** pinned right (mobile: both
  stacked centred at top 22%). **The delays are paced against the 1.8 s particle fly-in**:
  IMAGINE at 200ms, MANIFEST at 520ms, then the footer lands as the X settles — four glass
  pills (Generative AI / Motion Systems / Realtime 3D / Brand Identity — 9.5rem wide ≥ md,
  `--overlay-glass` + blur 6px, 1rem font; 2-col grid on mobile; **delay 950ms**), a hairline,
  a 34rem uppercase tagline **"Design, systems, and living interfaces. The machine is our
  medium — the vision stays human."** (**delay 1300ms**) opposite two CTAs (**delay 1600ms**):
  **Begin a Project** (solid black, 11.1875rem) and **Explore the Work** (glass +
  `--overlay-edge` border). Buttons re-enable pointer events.
- **Hand overlay:** top-left h2 (below the header, 38rem max) — **"Where intent takes form"**;
  bottom cluster: tagline **"Concept, motion, and interface — shaped with the machine, kept
  unmistakably human."** (27.5rem, delay 160) over three stat blocks (delay 280, left hairline
  border `--overlay-strong`, pl 1rem): **4.9 Client Rating / 240+ Projects Shipped / 16yr In
  Practice**; bottom-right a two-line right-aligned 4rem heading (desktop only) — **"Studio of
  the / next generation"** (second line +90ms).
- **Wave overlay** (all content in the right half; the wave falls lower-left): top-right
  right-aligned two-line h2 — **"Shaped for / what comes next"**; tagline **"Art direction,
  identity, and interactive web. AI is our instrument — the intent is ours."** (20rem, delay
  140); a 2×2 grid (41.625rem wide, gap 1rem, stretching to the bottom margin) of **solid white
  cards** (`background: var(--overlay-ink)`, black text, p 1rem, number top / description
  bottom): 01 "Generative brand systems, shaped end to end." / 02 "Realtime 3D — product films
  and configurators." / 03 "Interactive sites built around motion." / 04 "Art direction for
  AI-native products." **Exit:** the overlay root is clipped along the wipe line every frame —
  `clip-path: polygon(0 0, 100% 0, 100% r%, 0 l%)` where `l/r = (1 − uWipe ∓ 0.5·0.4)·100`
  (no clip while the line is below the frame) — its content translates down `7vh·w` with the
  wave view, and any remaining top-right sliver fades out over w ∈ [0.45, 0.72].
- **Tree overlay:** centred h2 **ENVISION** near the top (6.0625rem); two 20rem taglines pinned
  at mid-height, one per edge (mobile: one centred) — left: **"Have something in mind? We shape
  brands, products, and interactive worlds — research through launch."** (delay 160); right:
  **"Bring a rough idea or a finished brief. Either way the conversation starts in the same
  place."** (delay 240); bottom-centre **glass contact bar** (delay 340, `--overlay-glass` +
  blur 16px + `--overlay-edge` border, pl 2rem, py 0.25rem, row gap 2.6875rem ≥ md; stacked
  full-width on mobile): transparent Name and Email inputs (11.25rem, placeholder
  `--overlay-strong`), and a solid black **Start a Project** button (px 2.9375rem) with a
  0.375rem **green square** (`--overlay-accent`) that blinks forever on a spring (opacity
  1 ↔ 0.2, tension 90, friction 18). The bar re-enables pointer events; the form is a visual
  mock.

## The preloader / reveal

Full-screen `--overlay-solid` veil (z 70), centred column gap 1.5rem: **AURALIS** (1rem
extra-light uppercase, `tracking 0.5em`, matching indent), a 12rem × 1px track
(`--overlay-line`) whose white fill springs toward real load progress (three gates: hand loaded,
tree loaded, warm-up done — spring tension 260 friction 34), and a tabular-nums counter
`000 → 100` derived from the same spring value (0.75rem, `tracking 0.2em`, `--overlay-strong`).
When the bar itself reaches ≥ 0.995 (poll the spring on rAF), hold 260ms, then fade the veil out
(tension 70, friction 26) and **hand off**: the glyph's 1.8s fly-in starts, the flares stage
their entrance, and the hero copy plays in on its paced delays. Before starting the loop,
pre-compile the scene + tree view and pre-render one frame of each into the wipe targets so the
first scroll never hitches.

## Fixed parameters (bake these in)

Everything from the config, exactly: fog `#868e96` 25→130; backdrop `#aab2ba`→`#868e96`;
particle `#ffffff` (roughness 0.98, metalness 0, envMapIntensity 0.08); key/skyFill/groundFill
`#ffffff`/`#e6ecf2`/`#8a929a`; pink bloom particles `#b552ff` × 5.5; pointer `#b579ff` × 3.6,
radius 5, strength 6, relax 0.22, returnRelax 0.035; glyph count 32000, bars 16.8/3.1/3.4, size
0.12 ± 0.55, edgeNoise 0.5, spill 1.1 @ 0.045, rotation π/7, centre (0,11,0), bigFraction 0.04,
bigScale 2, seed 20260720; hand scale 7, size 0.1 ± 0.4, centre (0,8,−110), rotation −π/6,
density 12, sparse 0.55, scanGlow 6, seed 90210; tree scale 8, size 0.08 ± 0.4, centre
(0,9,−175), density 3, scanGlow 4.2, seed 71717; anchors hand (0,0,−110) / tree (0,0,−175);
lattice 7.6 × 18, 56 × 20 segments, opacity 0.32, centreY 10.5, heroScale 1.2, spin 0.08, build
seed 20260728; dust 700 / spread 38 / height 34 / size 0.3 / opacity 0.72 / seed 77; scatter
distance 26, radial 0.4, stagger 0.25, twist 0.3, drift 0.28, wander 0.8, towardCamera 0.22;
flight ribbons 3, radius 14, bend 0.8, bendWaves 2.2, turn 1.4, speed 0.5, axisY 14, vScale 0.5,
z −6 → −120, spread 0.5, thickness 0.2, exit 60/1.3/14/1.2; wave grid 336 × 150 @ 0.41, centre
(−8,33,−120), tilt 0.45, yaw 0.6, edgeFade 0.28, amplitude 5, freq 0.16/0.2, speed 0.5,
sizeBoost 0.9, troughShade 0, lift 2, transformRange [0.66, 0.99], seed 4242, vortex {strength
1, plateau 1.3, strands 4, spread 24, radius 2, height 46, meander 9, meanderFreq 1.6, coreBias
6, twist 5, spin 0.8, stagger 0.26, flash 2.2}; idle amplitude 0.16, speed 0.18, relax 0.05,
bigGlow 5; life {colour #b552ff; glyph 0.55/0.05/0.12/0.3, hand 0.45/0.04/0.045/0.12, tree
0.8/0.14/0.055/0.18 (flow/sway/rock/bob); cursorTilt yaw 0.07 pitch 0.04}; waveMotes {130,
(−8,18,−116), 34/10, rise 22, sway 0.9, speed 0.02–0.05, size 0.42, opacity 0.5, seed 5150};
treeMotes {110, (0,3,−175), 11/9, rise 20, sway 1.2, speed 0.025–0.06, size 0.5, opacity 0.65,
seed 6060}; sequence scrollVh 1180, dollyStart 0.02, arrive 0.56, treeStart 0.72, wipeRange
[0.74, 0.95], wipeTilt 0.4, wipeWaveDrop 0.07, wipeTreeDrop 0.045, treeScanRange [0.88, 0.99],
intro {seconds 1.8, spread 11, glow 4.2, netDelay 0.75, netWeaveSeconds 1.4}, camera fov 32,
roll 0.26 (peak bank, sin envelope), scatterRange [0.02, 0.28], netRange [0.02, 0.26]; bloom
threshold 3.5, strength 1.5, radius 1.9; vignette offset 1, darkness 1.25; exposure 1.

## Assets

Load each from the public bucket (all under
`https://storage.getlayers.ai/assets/auralis-7725b062e5/`):

- `hand-points.bin` — the hand point cloud (4173 × 3 uint16; decode constants above).
- `tree-points.bin` — the tree point cloud (50 000 × 3 uint16; decode constants above).
- `light_1.png` — full-frame diagonal light rays (the banking overlay flare).
- `light_2.png` — the bottom-left glint (plus-lighter).
- `light_3.png` — the bokeh flare that owns the wave act.
- `GoogleSansFlex_9pt-ExtraLight.ttf`, `GoogleSansFlex_9pt-Light.ttf`,
  `GoogleSansFlex_9pt-Regular.ttf` — the display face (weights 200/300/400).
[/hidecontent]
← 返回首页