---
title: SC ThreeJS Audio Particles
description: Audio-reactive Three.js particle field with curl-noise motion and an orbit camera.
url: https://secretocho.design/lab/sc-threejs-audio-particles/
type: SoftwareSourceCode
status: working
published: 2025-08-26
updated: 2025-08-26
---

# SC ThreeJS Audio Particles

> Audio-reactive Three.js particle field with curl-noise motion and an orbit camera.

component 01 / 28 · WORKING · featured

- **type**: Code Component
- **platform**: Framer
- **last update**: 2025-08-26
- **read time**: 12–16 min
- **license**: SecretoCho Design

![A still of the SC ThreeJS Audio Particles demo, as its module draws it with reduced motion.](https://secretocho.design/_astro/still.imxncnMl_Z20MBVh.webp)

*fig 00 · live demo, ported from **SC\_ThreeJSAudioParticles.tsx***

Audio-reactive WebGL particle field (Three.js) using curl/simplex-noise displacement and FFT bands for color/size, with mouse/gyro orbit camera.

SC ThreeJS Audio Particles is an audio-reactive particle field powered by Three.js and custom GLSL shaders. Particles swirl with curl-noise motion, expand and pulse to live microphone input, and color-shift across low/mid/high bands. Supports mouse orbit or gyroscope look controls and runs as a full-bleed WebGL canvas inside Framer.

Great for music heroes, landing backgrounds, VJ loops, or meditative reactive visuals.

- Visual
- Audio-Reactive
- Three.js

## Main

### Overview

- **Built With**: React, Three.js, WebGL, GLSL, Framer Code Component API
- **Layout Support**: Fully Responsive (fills container)
- **Orientation**: Full-bleed canvas visualization
- **Ideal Use Case**: Music visualizers, hero backgrounds, live show booths, generative art

### Tags

- three.js
- webgl
- glsl
- particles
- audio
- analyser
- shader
- curl-noise
- simplex
- mic
- gyro

### Code comment

```tsx
/**
 * Audio-reactive Three.js particles with GLSL curl/simplex noise.
 * Mouse/gyro orbit · Mic-driven uniforms for color/size/displacement.
 * Original anim © Jared Stanley · Framer port © SecretoCho Design, 2025
 * @framerDisableUnlink
 * @framerSupportedLayoutWidth any
 * @framerSupportedLayoutHeight any
 */
```

### Dependencies

- **Technologies**: Three.js, Web Audio API, DeviceOrientation API (optional)
- **Notes**: Requires HTTPS for mic/gyro permissions; no external state managers.

### Complexity

- **Logic Level**: Medium–High (shaders + audio analysis)
- **Runtime Cost**: Medium–High (GPU fragment/vertex + point sprites)
- **Component Size**: ~6–8 KB (raw TSX + shaders)
- **Notes**: Throughput scales with particleCount; tune for target devices.

## Guide

### Instructions

1. Drop **SC-ThreeJS AudioParticles** into a frame (it auto-sizes to the container).
2. Set **particleCount** (start 1500–3000 on laptops).
3. Choose **bgColor / lowColor / midColor / highColor** as GLSL vec3(r,g,b) values (0–1).
4. Adjust **camera**: cameraZ (orbit radius), FOV, near/far.
5. Interaction: enable **Mouse** (desktop) or **Gyro Control** (mobile).
6. Click once to grant **mic** permission and start audio reactivity.

### Examples / Presets

- Club Visual
  
  - **particleCount**: 3500
  - **cameraZ**: 1.4
  - **low**: vec3(0.1,0.4,1.0)
  - **mid**: vec3(1.0,0.4,0.1)
  - **high**: vec3(1.0,0.1,0.4)
  
- Calm Ambient
  
  - **particleCount**: 1800
  - **cameraZ**: 1.8
  - **low**: vec3(0.2,0.6,0.9)
  - **mid**: vec3(0.2,0.9,0.6)
  - **high**: vec3(0.9,0.2,0.6)
  
- Monochrome
  
  - **particleCount**: 2200
  - **colors**: vec3(1.0,1.0,1.0)
  - **bg**: vec3(0.0,0.0,0.0)
  

## Mechanics

### How it Works

- **Geometry**: random points on/in a sphere (cubic distribution with r = cbrt(rand)\*2).
- **Attributes**: positions also used as normals for directional cues.
- **Vertex Shader**:
  - Curl noise field + simplex noise → ordered + chaotic displacement.
  - Audio uniforms (uAudioLow/Mid/High) modulate expansion, movement, and size.
  - gl\_PointSize scales with audio and perspective.
- **Fragment Shader**: soft circular sprite with inner glow; mixes base and audio-tinted colors.
- **Audio**: Web Audio AnalyserNode (fftSize=512) → frequency averages for low/mid/high bands.
- **Controls**: mouse maps to spherical angles; **Gyro** uses DeviceOrientation (permission-gated on iOS).
- **Loop**: requestAnimationFrame updates uniforms, camera, and renders.

### Performance & Tuning

- **particleCount** dominates cost. Targets:
  - Mobile: 1200–2200 · Laptops: 2000–4000 · Desktops: 4000–8000.
- Prefer smaller **pointSize** via shader (already dynamic) over more particles.
- Avoid excessive **bg blur/overlays** atop the canvas; they force compositing.
- Run in a container sized to need; massive full-screen on 5K displays will be heavy.
- Consider lowering analyser **fftSize** if you fork (256) for fewer allocations.

### Compatibility & Limits

- **HTTPS required** for mic + sensors.
- **User gesture** required to start audio on Safari/iOS (first click handled).
- Works in modern evergreen browsers with WebGL; no SSR rendering.

## Quality

### Accessibility

- Decorative by default: wrap with aria-hidden="true" if it conveys no information.
- If used behind text, ensure sufficient contrast and avoid rapid strobe effects.

### Security & Privacy

- **Permissions**: Mic access via getUserMedia({audio:true}); Gyro access via DeviceOrientationEvent.requestPermission() on iOS.
- **Data**: Audio stays in-browser; no network calls, logging, or storage.
- **Hardening**: Events are removed on unmount; consider disposing Three.js objects (renderer.dispose(), geometry.dispose(), material.dispose()) in a fork for long-lived pages.

## Reference

### Props

| name | type | default | description |
| --- | --- | --- | --- |
| particleCount | number | 2500 | Number of particles in the point cloud (higher = richer, slower) |
| enableMouse | boolean | true | Enable mouse-driven camera orbit on desktop |
| gyroControl | boolean | false | Enable device-orientation orbit on mobile (prompts for permission on iOS) |
| cameraZ | number | 1 | Camera radius/orbit distance from origin |
| cameraFov | number | 75 | Perspective field of view in degrees |
| cameraNear | number | 0.1 | Camera near clipping plane |
| cameraFar | number | 5000 | Camera far clipping plane |
| bgColor | string | vec3(0.0, 0.0, 0.0) | Scene background color (GLSL vec3) |
| lowColor | string | vec3(0.1, 0.4, 1.0) | Color influence for low frequencies |
| midColor | string | vec3(1.0, 0.4, 0.1) | Color influence for mid frequencies |
| highColor | string | vec3(1.0, 0.1, 0.4) | Color influence for high frequencies (use GLSL vec3) |

### Notes & Tips

- Use a **HEX → GLSL vec3** converter for colors (values are 0–1 floats).
- For kiosk setups, lock **gyroControl=false** and prefer **mouse** to avoid noisy sensor drift.
- Camera feeling “flat”? Increase cameraZ slightly and lower FOV to 60–70.

### Troubleshooting / FAQ

Nothing reacts to audio.

Click the canvas to grant mic permission; check site is served over HTTPS; verify system input level.

Movement is jittery on mobile.

Disable **Gyro Control** or hold device steady; sensor noise varies by hardware.

Black screen.

Ensure WebGL is enabled; try lowering particleCount.

Colors look wrong.

Provide vec3(r,g,b) with values 0–1 (e.g., vec3(1.0,0.0,0.0) for red).

## Source

### Header comment

*SC\_ThreeJSAudioParticles.tsx · lines 4–10 · header comment*

```tsx
/**
 * Audio-reactive particle system using Three.js + GLSL curl/simplex noise.
 * Mouse or gyroscope camera orbit; mic-driven uniforms for color/size/displacement.
 * The animation by [© Jared Stanley](https://codepen.io/jaredstanley)
 * Modified and converted to Framer by [© SecretoCho Design](https://secretocho.design), 2025
 * Having Issues? [Contact Us](https://secretocho.design/help)
 */
```

[props \[REF\]](#reference)

### Key excerpt

The vertex shader’s main(): curl noise moves the points in order, simplex noise in chaos, and the three audio bands mix the two, tint the colour and size each point.

*SC\_ThreeJSAudioParticles.tsx · lines 168–198 · glsl*

```glsl
void main(){
vec3 curl=curlNoise(position*0.5+uTime*0.1);
float noise=snoise(position*0.5+uTime*0.2);
float chaos=(uAudioLow+uAudioMid+uAudioHigh)*0.7;
vec3 randomOffset=vec3(
snoise(position+vec3(uTime*2.0,0.0,0.0)),
snoise(position+vec3(0.0,uTime*2.0,0.0)),
snoise(position+vec3(0.0,0.0,uTime*2.0)));
float expansion=1.0+uAudioLow*0.5;
vec3 basePosition=position*expansion;
vec3 orderedMovement=curl*(0.3+uAudioMid*0.20)+normal*(noise*0.42);
vec3 chaoticMovement=randomOffset*chaos*2.0;
vec3 displaced=basePosition+mix(orderedMovement,chaoticMovement,chaos*0.7);
displaced+=curl*sin(uTime*10.0)*uAudioHigh*0.2;
vec3 baseColor=vec3(
0.5+0.5*sin(curl.y+2.0),
0.5+0.5*sin(uTime*1.0+curl.y),
0.5+0.5*sin(uTime*0.1+curl.z+4.0));
vColor=baseColor;
vColor=mix(vColor,lowColor,uAudioLow*0.057);
vColor=mix(vColor,midColor,uAudioMid*0.057);
vColor=mix(vColor,highColor,uAudioHigh*0.057);
vAudioMid=uAudioMid;
vec4 mvPosition=modelViewMatrix*vec4(displaced,1.0);
gl_Position=projectionMatrix*mvPosition;
float size=5.0;
size+=uAudioLow*1.0;
size+=uAudioMid*5.0;
size*=(1.0+uAudioHigh);
gl_PointSize=size*(1.0 / -mvPosition.z);
}`
```

## Credits

### Credits

Original animation © **Jared Stanley**; Three.js/GLSL adaptation for Framer © **SecretoCho Design** (2025).

- **inspired by**: Jared Stanley · codepen.io/jaredstanley
- **license**: SecretoCho Design

SecretoCho Design 2025

### Disclaimer

Provided “as is” for visual/UX purposes. You’re responsible for obtaining user consent for microphone/sensor usage and validating performance, accessibility, and licensing in your environment.

## Proves

- [Creative coding](https://secretocho.design/about/#capabilities)

## Hiring a design engineer?

I’m open to senior design engineer roles: remote from Belgrade, or relocation with visa sponsorship. Write to me, or read the CV first. Client work stays selective, one project at a time.

[sc@secretocho.design](mailto:sc@secretocho.design)

[read the CV](https://secretocho.design/cv/) · [LinkedIn](https://www.linkedin.com/in/secretocho/)
