Stack: Angular app with Three.js embedded directly (~1MB in the JS bundle). No GSAP, no Lottie, no external animation libraries.
Three.js + scroll-driven interpolation. The cube is not one mesh breaking apart — it's two sets of PlaneGeometry faces (split state vs. assembled state) with the visible cube interpolating between them:
- Split state: 4 plane meshes at scattered positions/rotations
- Assembled state: 6 plane meshes arranged as cube faces at +/-0.5 offsets
- On scroll, it lerps positions and slerps quaternions between the two states using eased scroll progress:
E = 0.5 * (1 - Math.sin(Math.PI * (0.5 - scrollProgress))); // sinusoidal ease mesh.position.lerp(assembledPosition, E); mesh.quaternion.slerp(assembledQuaternion, E);
- Wireframe edges use Three.js's
LineMaterial/LineSegmentsGeometry(fat-line implementation) IntersectionObservergates the render loop on/off when the canvas is visibleImprovedNoise(Perlin noise) drives subtle idle floating
Custom GLSL3 shaders with a two-pass rendering pipeline:
Pass 1 — Render backface normals to an offscreen WebGLRenderTarget:
FragColor = vec4(worldNormal, 1.0); // BackSide renderingPass 2 — Per-channel refraction with different indices of refraction:
const float iorRed = 1.0 / 1.15;
const float iorGreen = 1.0 / 1.15;
const float iorBlue = 1.0 / 1.18; // slightly different -> color split
for (int i = 0; i < 16; i++) {
float slide = float(i) / 16.0 * 0.1;
color.r += texture(tMap, uv + refract(eye, normal, iorRed).xy * 0.3).r;
color.g += texture(tMap, uv + refract(eye, normal, iorGreen).xy * 0.5).g;
color.b += texture(tMap, uv + refract(eye, normal, iorBlue).xy * 0.5).b;
}- Blended normals (67% front + 33% back) create thickness-dependent refraction — edges refract more
- 16-iteration loop with progressive slide offset for smoother dispersion
- Fresnel rim lighting:
pow(1 - abs(dot(eye, normal)), 3)tints edges with an accent color
Pure CSS. No JS. Seven stacked <div>s, each with increasing backdrop-filter: blur() and a shifted CSS mask gradient:
| Layer | Blur | Mask band |
|---|---|---|
| 1 | 1px |
0-40% |
| 2 | 2px |
10-50% |
| 3 | 4px |
15-60% |
| 4 | 8px |
20-70% |
| 5 | 16px |
40-90% |
| 6 | 32px |
60-80% |
| 7 | 64px |
70-100% |
Each layer only applies blur within its mask band. The container is transform: rotate(180deg) so the blur increases downward from the header. Overlapping masks create a smooth gradient from sharp to fully blurred.
Pure CSS using the Motion Path spec (offset-path). No JS animation at all:
.shine::before, .shine::after {
offset-path: border-box; /* path = element's border perimeter */
offset-anchor: 100% 50%;
background: radial-gradient(/* accent-colored glow blob */);
filter: blur(12px);
animation: trail 15s infinite linear;
}
.shine::after {
offset-distance: 50%; /* starts opposite side */
}
@keyframes trail {
to { offset-distance: 100%; }
}Two pseudo-elements with radial gradient blobs travel along the element's border box perimeter via offset-distance: 0% -> 100%. The ::after starts at 50% so they're always on opposite sides. filter: blur(12px) softens them into a glow. On :focus-within, the blob expands to 800px width for an intensified effect.
| Effect | Tech | Libraries |
|---|---|---|
| Cube animation | WebGL + scroll | Three.js (embedded) |
| Glass/chromatic aberration | Custom GLSL3 shaders | Three.js RawShaderMaterial |
| Progressive blur | backdrop-filter + mask |
None — pure CSS |
| Border shine | offset-path: border-box |
None — pure CSS |