enhance-web-web3d — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited enhance-web-web3d (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
### Which enhance skill? (surface router)
>
| Your surface | Use | |:-------------|:----| | Web product page / dashboard — composition, hierarchy, spacing, motion |enhance-web-ui| | Web product page — UX heuristics, flows, data wiring |enhance-web-ux| | Web landing / marketing / portfolio (greenfield, anti-slop) |enhance-web-landing| | Web existing site upgrade (audit-first, preserve behavior) |enhance-web-redesign| | Web 3D / WebGL / cinematic scroll on an existing site (audit-first) |enhance-web-web3d| | React Native screen (Expo / bare) |mobile-rn-screen| | Capacitor / hybrid shell (one web app shipped to iOS + Android) |enhance-capacitor-ui(axis architecture first) → then the web or rn skill | | Repo README showcase |enhance-readme|
>
You are here: `enhance-web-web3d`. Native iOS/Android (SwiftUI / Compose, no web layer) is out of scope for all of these — use Apple HIG / Material directly.
Add Three.js / React Three Fiber scenes, GSAP scroll choreography, and physics motion to an existing web project — without rewriting it, breaking the build, blowing the performance budget, or shipping a blank canvas to users on weak devices or with WebGL disabled.
This is not a "drop a spinning cube on the homepage" skill. It audits the repo first, decides whether 3D actually serves the page, picks the smallest library combination that fits the existing stack, and ships the effect with fallbacks and reduced-motion support from the first commit.
For greenfield / from-scratch 3D library mechanics (raw Three.js scene setup, R3F primitives, shader authoring), this skill points you at the deeper references but stays focused on integrating into a live repo. For pure visual composition (no 3D) useenhance-web-ui; for anti-slop landing pages useenhance-web-landing.
When applied to an existing project, follow this sequence. Never skip the audit and jump to code — that is how 3D ends up as a 4 MB hero nobody on mobile can load.
SSG / SPA), styling system, existing motion/3D libraries, build tool, and the current performance baseline.
surface and where. Reject 3D-for-3D's-sake.
scroll-story, background ambience) and the constraints around each.
combination that fits the existing stack.
fallbacks, reduced-motion, and SSR/hydration safety in the same change.
reduced-motion paths, confirm Lighthouse / Web Vitals didn't regress.
Before touching any library, answer these. If you can't justify the effect, the right move is enhance-web-ui or enhance-web-landing, not WebGL.
A product configurator, a data-driven globe, a hero that shows the actual product in 3D — these earn their cost. A generic floating-blob background usually does not.
more than a dashboard a user opens 40× a day. Name the budget (KB, LCP, INP, target FPS) before designing.
mobile fallback first, then enhance up. Desktop-only internal tool → you have more headroom.
A pre-rendered <video>, an animated SVG, a CSS scroll effect, or a high-quality image sequence often beats live WebGL. The DEV community lesson holds: a scripted DOM/GSAP walkthrough can be under 40 KB gzip vs a multi-MB MP4/GIF — reach for real WebGL only when interactivity or true 3D depth is the point.
prefers-reduced-motion user and a no-WebGL user see, you're not ready to build.
Anti-slop guardrail. A spinning torus knot, a particle field with no meaning, or a tilt-on-mouse card that fights scrolling are the 3D equivalent of the purple-gradient hero. Motion and depth must explain, demo, or brand — never just sparkle.
Read the entry point, layout, and any existing animated component. Then record:
FRAMEWORK: [Next.js (app/pages?) / Vite+React / Astro / Remix / SvelteKit / vanilla]
RENDER MODEL: [SSR / SSG / CSR — matters for hydration + dynamic import]
REACT?: [yes → R3F is on the table | no → vanilla three.js]
STYLING: [Tailwind v3/v4 / CSS modules / styled-components / vanilla]
BUILD TOOL: [Vite / Webpack / Turbopack / esbuild — affects code-split + worker setup]
EXISTING MOTION: [framer-motion/motion? GSAP? Lenis? CSS only?]
EXISTING 3D: [three? @react-three/fiber? drei? none?]
PERF BASELINE: [current LCP / bundle size / Lighthouse, if measurable]
ASSET PIPELINE: [is there a /public model dir? a CDN? Draco/KTX2 already set up?]Check the dependency manifest before importing anything — reuse what's installed, match the installed major version (three, @react-three/fiber, gsap, motion vs framer-motion), and never hallucinate an import.
Search for prior art and guard comments:
rg -l "three|@react-three|gsap|ScrollTrigger|Lenis|framer-motion|\"motion\"" --type ts --type tsx package.json
rg -n "useGSAP|registerPlugin|<Canvas|useFrame|frameloop" src/
rg -n "prefers-reduced-motion|matchMedia|DO NOT|deprecated" src/Map the page to one (rarely more than two) of these archetypes. Each has a different cost/benefit and a different stack.
| Opportunity | What it is | Earns its cost when | Default stack |
|---|---|---|---|
| Hero scene | First-fold 3D centerpiece | The product is visual/physical | Three.js or R3F + a light entrance tween |
| Product viewer / configurator | Rotate/zoom/customize a model | Users decide based on the object | R3F + drei (OrbitControls, Environment) |
| Scroll story (pinned) | Camera/model animates as you scroll | There's a narrative or process to reveal | GSAP ScrollTrigger (pin + scrub) driving the scene |
| Ambient background | Subtle depth behind content | Brand texture, low-distraction | Cheap shader / instanced particles, frameloop="demand" |
| Data viz in 3D | Globe, graph, point cloud | The data is inherently spatial | R3F + instancing; or 2D if it reads better flat |
| Micro-interaction | Hover/drag physics on a card | Delight on a key CTA | Motion / React Spring (often no Three.js at all) |
Rule: one focal 3D moment per page beats five. If everything moves, nothing is special — the same hierarchy logic as enhance-web-ui applies in three dimensions.GSAP is 100% free for all use since April 2025 (all former Club/bonus plugins — ScrollTrigger, SplitText, MorphSVG, etc. — included), so plugin licensing is no longer a reason to avoid it.
| Use case | Recommended stack | Rationale |
|---|---|---|
| Marketing page, scroll-driven 3D | Three.js (or R3F) + GSAP ScrollTrigger + HTML/Motion overlays | GSAP owns scroll orchestration; clean layer separation |
| React app, interactive 3D viewer | R3F + drei + Motion | Declarative, state-driven, component-based |
| Timeline-choreographed sequences | R3F + GSAP (timeline in useGSAP) | GSAP timeline control over R3F objects |
| Physics-y drag / momentum / gestures | R3F + React Spring (@react-spring/three) | Spring physics feel natural |
| High-count particles / instances | Three.js + GSAP, InstancedMesh/BatchedMesh | Imperative control, minimal overhead |
| Smooth-scroll feel under GSAP | add Lenis (lenis / react-lenis) | Inertia scroll that ScrollTrigger can drive |
| Pure UI delight (no real 3D) | Motion or React Spring only | Don't pull in WebGL for a hover effect |
| Non-React repo | vanilla three.js + GSAP | R3F requires React; don't add React for one scene |
One animation system per property. GSAP or React Spring or Motion animating a given transform — never two on the same property. Coordinate by timing or split properties (GSAP drives position, Spring drives scale).
Pick the pattern that matches the stack from Phase 3. Full, current code lives in the foundation references (see Research & Foundation Skills); below are the load-bearing shapes and the integration glue.
Best for non-React repos and marketing sites. Three layers: a 3D layer (scene/camera/render loop), an animation layer (GSAP ScrollTrigger), and an HTML/UI overlay. The scene exposes refs (camera, key meshes); GSAP animates those refs on scroll; HTML sits on top.
// scroll.js — GSAP drives the scene's exposed refs
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
export function initScrollAnimations({ camera, model }) {
const tl = gsap.timeline({
scrollTrigger: { trigger: "#stage", start: "top top", end: "+=3000", scrub: 1, pin: true },
});
tl.to(camera.position, { x: 5, z: 10, onUpdate: () => camera.lookAt(0, 0, 0) })
.to(model.rotation, { y: Math.PI * 2 }, 0);
return () => tl.scrollTrigger?.kill(); // always return teardown
}Best for React apps with interactive, state-driven 3D. <Canvas> holds the scene; motion.div overlays carry HTML. Keep per-frame work in useFrame mutating refs, never React state.
// Lazy-load the canvas so WebGL never blocks first paint / SSR
const Scene = dynamic(() => import("./Scene"), { ssr: false, loading: () => <Poster /> });
<Canvas camera={{ position: [0, 2, 5], fov: 45 }} dpr={[1, 1.5]} frameloop="demand">
<Suspense fallback={null}><Scene /></Suspense>
</Canvas>R3F renders; GSAP choreographs. Create the timeline inside useGSAP (scoped, auto-cleaned) and target the R3F object's .current.
import { useGSAP } from "@gsap/react";
useGSAP(() => {
const tl = gsap.timeline({ scrollTrigger: { trigger: ref.current, scrub: 1 } });
tl.to(groupRef.current.position, { y: 2, ease: "power2.inOut" });
}, { scope: ref }); // useGSAP reverts on unmount — handles Strict Mode double-invoke@react-spring/three for drag/momentum/gesture feedback that should feel weighty. Spring owns scale/position; nothing else touches those props.
WebGL is expensive and degrades hard on weak devices. Apply these from the first commit, not after a complaint.
Render loop
dpr={[1, 1.5]} (R3F) / renderer.setPixelRatio(Math.min(devicePixelRatio, 2)).frameloop="demand" + invalidate() on change; vanilla → only render when something changed or controls are active.IntersectionObserver stops the loop when the canvas leaves the viewport.useFrame — mutate refs. State in the loop forces 60 fps re-renders.Draw calls & geometry
InstancedMesh / BatchedMesh for repeated objects; merge static geometry.Assets
colorSpace (SRGBColorSpace).Modern renderer (optional, current)
import { WebGPURenderer } from "three/webgpu" ships one renderer with automatic WebGL2 fallback. Use forceWebGL: true to test the fallback path. No urgent migration if a WebGL2 app already runs smoothly.Main thread (advanced)
OffscreenCanvas + Web Worker to keep the main thread free for UI/INP.Cleanup
dispose() every geometry / material / texture / render target on unmount; renderer.dispose(). Kill GSAP tweens/ScrollTriggers (useGSAP does this for you).A 3D enhancement that breaks for some users is a regression, not an upgrade.
prefers-reduced-motion: disable scrollscrub / auto-rotation / parallax, or swap to a static poster frame. Gate at the source so the whole scene honors it.
const reduce = typeof window !== "undefined"
&& window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// reduce ? render static poster / no scrub : full experiencerender a static image/<video>/SVG that preserves the visual language and the conversion path. Never a blank canvas.
fewer particles, no heavy post-processing, fewer transparent layers.
text content live in real DOM (overlay), not as click targets inside the canvas — screen readers and keyboard users need them, and it protects INP.
canvas must not steal keyboard focus or block tabbing.
WebGL init — show a poster immediately, hydrate the scene after.
The most common way 3D breaks an existing app is server rendering and React Strict Mode.
dynamic(() => import("./Scene"), { ssr: false }). Guard any window/document access with a client check.useEffect — it scopesselectors and auto-reverts animations on unmount, surviving route changes and Strict Mode's double-invoke. Never create tweens in the render body (a new animation per re-render).
listeners, IntersectionObservers, disposables.
lenis.on("scroll", ScrollTrigger.update) + ticker wiring so scroll position stays in sync.
| Pitfall | Fix |
|---|---|
| Two libraries animate the same property | One system per property; split or sequence |
React state mutated in useFrame | Mutate refs; lift state out of the loop |
| Scene renders every frame while idle/off-screen | frameloop="demand" + IntersectionObserver pause |
| GSAP tween created in render body | Wrap in useGSAP / contextSafe |
| Blank canvas on no-WebGL / slow device | Feature-detect + poster/video fallback |
| WebGL initialized on server | ssr: false dynamic import; window guards |
| Multi-MB model on the hero | Draco/KTX2 + lazy-load + poster + device-tier scene |
| Scroll jank under ScrollTrigger | scrub numeric (smoothing), Lenis, refs not state |
| Memory grows on route change | Dispose geometries/materials/textures + kill tweens |
| Parallax/tilt fights native scroll | Reduced-motion gate + clamp; don't hijack the wheel |
renderer.info / stats-gl); DPR capped.prefers-reduced-motion path verified (motion off / poster shown).forceWebGL + simulated failure) — graceful fallback, never blank.window is not defined, no hydration mismatch, no Strict Mode double-mount leak.three, @react-three/fiber, @react-three/drei, gsap, @gsap/react, motion/framer-motion, @react-spring/three, lenis).ssr: false), not narration.enhance-web-ui.enhance-web-landing.enhance-web-redesign.enhance-web-ux.design-motion / design-motion.Foundation / deeper mechanics (in this repo):
enhance-web-web3d — Three.js, R3F, shaders, particle systems (greenfield mechanics).design-motion — Framer Motion / Motion, GSAP, CSS animation patterns.design-motion — physics-y micro-interactions and delight.audit-performance — Core Web Vitals (LCP/INP/CLS), bundle analysis.audit-accessibility — reduced-motion, keyboard, screen-reader compliance.Current external anchors:
useGSAP() (@gsap/react) for React cleanup/Strict-Mode safety. <https://gsap.com/resources/React/>useFrame. <https://r3f.docs.pmnd.rs/advanced/scaling-performance>forceWebGL to test. <https://threejs.org/manual/en/webgpurenderer.html>useGSAP cleanup, hydration safety. <https://mirax.cc/articles/cinematic-3d-scroll-gsap-nextjs-react-three-fiber>OffscreenCanvas/Web Worker + instancing for stable 60 FPS. <https://www.krapton.com/blog/boosting-react-three-fiber-mobile-performance-in-2026-a-deep-dive-d6105c>Adapted and generalized from theweb3d-integration-patternsmeta-skill in freshtechbro/claudedesignskills (and itsthreejs-webgl/gsap-scrolltrigger/react-three-fiber/motion-framer/react-spring-physicsfoundation skills) into an audit-first, repo-elevation skill in theenhance-web-*family.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.