SuperAPI AI Directory

threejs-particle-morph

Writing & research

View repository

Updated 10-16 01:14

About this skill

Skill: threejs-particle-morph

Pure Three.js component — thousands of particles scattered randomly in 3D space, gradually converging into an image sampled from pixels when the user scrolls, and dispersing again when scrolling back.

WHAT

Purpose and context

  • Purpose: build a Three.js particle cloud (THREE.Points + BufferGeometry) that converges from random positions into an image/logo based on page scroll progress and disperses again when scrolling back — prefer copying the original source, the vanilla version in assets/ is the fallback.
  • Trigger (when to use):
    • Hero section/portfolio needs an "image assembled from particles" effect when scrolled to.
    • User says "particle image", "particles converging into an image", "point cloud morph", "three.js particle scroll".
  • Non-goals: NOT used for seamless zoom-blur-style image transitions (that is blur — shader on 1 plane, no discrete particles) and NOT used for subtle scroll effects that don't need WebGL (that is scroll-effects). Do not rewrite the author's original code.

Mental model

ảnh nguồn → canvas ẩn (sampleResolution) → getImageData + stride + alphaThreshold → targets (+ màu) · starts random → scroll → targetProgress → progress (damping) → position = lerp(starts, targets, progress) → PointsMaterial render.

Input and output contract

Field Required? Meaning
In source image/logo (imageUrl) no missing → demo draws sample text onto a hidden canvas to sample
In particleCount, particleSize, sampleResolution, alphaThreshold no initialization parameters ParticleMorph
In project constraints (is pulling external libs allowed?) yes decides the original version (default) or assets/ vanilla
Out component in the project yes the original file copied as-is (keeping the attribution comment) or assets/demo.* adapted; there is destroy()
Out verify evidence yes screenshots at progress 0/0.5/1 + clean console

Rules and capabilities

  • RULE-01 (MUST): Performance: the more particles, the heavier the CPU load (the loop that writes position every frame) as well as the GPU. Suggested safe threshold for mid-range machines: ≤ 5,000–10,000 particles. Measure with DevTools Performance/FPS meter BEFORE increasing particleCount, don't guess — especially when adding particles for a large/detailed image (increasing sampleResolution also increases the sampling cost at init(), even though it only runs once).
  • RULE-02 (MUST): Three containers (three) must be installed via npm (npm install three) and imported via a bundler for production — the demo uses importmap pointing to a CDN (unpkg.com/three@0.169.0) ONLY for running standalone without a bundler, do not use the old CDN version pinned to a version like blur used to do. When wiring into a real project: delete <script type="importmap">, npm install three, change import * as THREE from 'three' to import via bundler.
  • RULE-03 (SHOULD): alphaThreshold/brightness filter in _buildSamples() excludes transparent/dark background pixels — a transparent-background image (PNG logo) gives cleaner results than a solid-background image (JPEG); a solid-background image will sample the background too unless cropped first.
  • RULE-04 (MUST): Verify with playwright-verify — capture at progress 0/0.5/1 (scroll to the 3 markers), read the console (WebGL context errors are usually silent, do not throw a clear JS error).
  • RULE-05 (MUST): Use the ORIGINAL first — copy the file as-is, do not rewrite it; keep the attribution comment on the first line; only change content/images/colors; run locally over HTTP, not file://.
  • Capabilities: read/copy assets from a public external repo; write front-end files into the project; run the page over HTTP + a headless browser to capture/read the console.

Failure boundaries

  • Cannot open the original link / cannot pull the external lib → use assets/ vanilla (branch B01).
  • FPS drops when increasing particleCount → blocked the increase further: re-measure, keep ≤ 5,000–10,000 particles.
  • Console reports a WebGL context error (usually silent) → failed verify, not handed off.
  • Solid-background image samples the whole background → crop first or use a transparent-background PNG.

HOW

Main workflow

Step Type Inputs Action Outputs/exit Failure/next
W01 judgment project constraints Choose source: original Rheinmir/uiux-asset (default) or assets/ vanilla chosen source cannot pull external lib → B01
W02 effect running link + code Open the link run to see the correct effect → copy the file as-is .html (+ _assets/) into the project file in the project, with the attribution comment remaining —
W03 effect file Only change content/images/colors (or imageUrl/particleCount/particleSize with the vanilla version) adapted component —
W04 deterministic page over HTTP Verify with playwright-verify: capture progress 0/0.5/1, read the console 3 images + clean console WebGL/FPS error → fix, repeat W03
W05 effect real project Wire production: delete importmap, npm install three, import via bundler build runs —

Step-by-step details (source of truth for W01–W05):

Original — use FIRST (copy the file as-is, don't rewrite)

The author's ORIGINAL VERBATIM code (public CodePen = MIT) is in an external repo Rheinmir/uiux-asset — live gallery: https://rheinmir.github.io/uiux-asset/scroll-effects/ . Process: open the link run to see the exact effect → copy the file as-is .html (+ the folder _assets/ if the file references it) into the project → keep the attribution comment on the first line → only change the content/images/colors. Run locally over HTTP, not file://. git clone --depth 1 https://github.com/Rheinmir/uiux-asset if you need the whole set offline.

Original Pen — author Link
Scroll-Driven Particle WebGL Image Matrix — ycw run · code
GSAP ScrollSmoother and Three.js — cmalven run · code
GSAP ScrollTrigger Disintegration Effect — dev_loop run · code

assets/ in this skill is just a self-written vanilla pared-down version — use it when the project must NOT pull in external libraries; by default use the original above.

Assets

assets/demo.html + assets/demo.css + assets/demo.js — read then ADAPT (change imageUrl, particleCount, particleSize when initializing ParticleMorph). The demo draws sample text onto a hidden canvas to sample when no imageUrl, so it runs immediately without images/network.

Mechanism

  • Image sampling: draw the source image (or a hand-drawn canvas) onto a hidden <canvas> sized sampleResolution, read getImageData(). Iterate pixels with a step size (stride computed from the desired particleCount versus the total pixels) — do NOT take every single pixel, too heavy — keep pixels bright enough/not transparent (alphaThreshold) as destination coordinates + RGB color.
  • Particle: each sample coordinate = 1 particle in the THREE.Points (BufferGeometry attribute position). Initial position (starts) is random within a large volume of space ("explosion"), destination position (targets) is the sampled pixel coordinate, stored in parallel in two separate Float32Array (not two GPU attributes).
  • Scroll → progress: a single scroll listener, rAF-throttled (no GSAP/ScrollTrigger — the demo is deliberately written in vanilla to avoid dependency overlap with gsap-scrolltrigger-pin), computes targetProgress from scrollY / (scrollHeight - innerHeight).
  • Lerp each frame: progress is gradually smoothed toward targetProgress (simple damping, not directly animating scrollY), then each element position[i] = lerp(starts[i], targets[i], progress), set geometry.attributes.position.needsUpdate = true.
  • Color + size: PointsMaterial({ size, sizeAttenuation: true, vertexColors: true }) — keeps the original image color via the static color attribute (set once at build time, does not change with progress).
  • Cleanup: written as a class with destroy() — cancel requestAnimationFrame, remove the scroll/resize listener, call geometry.dispose(), material.dispose(), renderer.dispose().

Branches

ID Kind Guard Behavior Skip / failure Rejoin
B01 conditional_required the project is NOT allowed to pull external libs use assets/demo.html + assets/demo.css + assets/demo.js, ADAPT the init parameters ParticleMorph — W04
B02 capability_optional needs the whole offline set git clone --depth 1 https://github.com/Rheinmir/uiux-asset if there is network access to the gallery → skip W02

Validation and stopping

Verify by actually running it: playwright-verify capture 3 scroll milestones + read the console (WebGL errors are usually silent); measure performance with DevTools Performance/FPS meter, do not guess. Visual review: the image converges into the right shape at progress 1, disintegrates at 0. Stop when the 3 images are correct + the console is clean.

Examples

  • Positive: a hero portfolio needs a PNG logo to converge when scrolling → copy exactly scroll-driven-particle-webgl-image-matrix.html from the gallery, keep the attribution comment, change the logo image → capture 0/0.5/1 to see scattered particles → half the image → full logo, console clean.
  • Boundary/failure: the project forbids external CDN libs → use assets/demo.js, particleCount 30,000 makes the FPS drop → lower to ≤ 10,000 based on measurement, then verify again.
  • Boundary: the user wants to transition image A→B with a zoom-blur effect on a single plane → do not use this skill, switch to blur.

Origin