agentleFS
soilmasssoilmass/motion-design-agent/skills/audio-reactive/SKILL.md

audio-reactive

Skill7 starsChanged 10 months agoMITView on GitHub

Audio-reactive animations including beat synchronization, frequency visualization, and sound-driven motion. Use when creating music visualizers, audio-responsive UI, or rhythm-based animations. Provides tokens for frequency bands, sensitivity, and response mapping.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    No additional references needed for this skill

    line 362

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Audio-Reactive

Animations driven by audio analysis and beat detection.

Standards Reference

StandardDocumentation
W3C DTCGdesigntokens.org
Web Audio APIdeveloper.mozilla.org/Web/API/Web_Audio_API
AnalyserNodedeveloper.mozilla.org/Web/API/AnalyserNode

Quick Reference

Load from: tokens/audio.tokens.json

Frequency Bands

TokenRangeUse
frequency.bass60-250HzKick drums
frequency.mid500-2000HzVocals
frequency.high4000-8000HzCymbals

Sensitivity Levels

TokenValueUse
sensitivity.subtle0.5Gentle response
sensitivity.normal1Standard
sensitivity.high2Dramatic

Beat Detection

TokenValueUse
beat.threshold.low0.3Detect subtle beats
beat.threshold.normal0.5Standard detection
beat.decay.fast0.9Quick response

Selection Guide

What audio effect?
│
├── BEAT SYNC → Pulse on rhythm
│   ├── Kick drum → frequency.bass
│   ├── Snare → frequency.mid
│   └── Hi-hat → frequency.high
│
├── FREQUENCY VIZ → Spectrum display
│   ├── Simple bars → bar-count.low (16)
│   ├── Detailed → bar-count.medium (32)
│   └── High-res → bar-count.high (64)
│
├── RESPONSIVENESS
│   ├── Subtle → sensitivity.subtle + smoothing.heavy
│   ├── Normal → sensitivity.normal + smoothing.medium
│   └── Reactive → sensitivity.high + smoothing.light
│
└── ANIMATION TYPE
    ├── Scale → response.scale-min to scale-max
    ├── Opacity → response.opacity-min to opacity-max
    └── Position → response.position-range

Patterns

Audio Analyzer Setup

JavaScript:

class AudioReactiveEngine {
  constructor(audioElement) {
    this.audioContext = new AudioContext();
    this.analyser = this.audioContext.createAnalyser();

    // Configure analyser
    this.analyser.fftSize = 256;
    this.analyser.smoothingTimeConstant = 0.75; // {motion.audio.smoothing.medium}

    // Connect audio source
    const source = this.audioContext.createMediaElementSource(audioElement);
    source.connect(this.analyser);
    this.analyser.connect(this.audioContext.destination);

    // Data arrays
    this.frequencyData = new Uint8Array(this.analyser.frequencyBinCount);
    this.timeData = new Uint8Array(this.analyser.frequencyBinCount);
  }

  getFrequencyData() {
    this.analyser.getByteFrequencyData(this.frequencyData);
    return this.frequencyData;
  }

  getAverageFrequency(startHz, endHz) {
    const nyquist = this.audioContext.sampleRate / 2;
    const startIndex = Math.floor((startHz / nyquist) * this.frequencyData.length);
    const endIndex = Math.floor((endHz / nyquist) * this.frequencyData.length);

    let sum = 0;
    for (let i = startIndex; i <= endIndex; i++) {
      sum += this.frequencyData[i];
    }
    return sum / (endIndex - startIndex + 1) / 255;
  }

  getBass() {
    return this.getAverageFrequency(60, 250); // {motion.audio.frequency.bass}
  }

  getMids() {
    return this.getAverageFrequency(500, 2000); // {motion.audio.frequency.mid}
  }

  getHighs() {
    return this.getAverageFrequency(4000, 8000); // {motion.audio.frequency.high}
  }
}

Beat Detection

JavaScript:

class BeatDetector {
  constructor(analyser) {
    this.analyser = analyser;
    this.threshold = 0.5; // {motion.audio.beat.threshold.normal}
    this.decay = 0.95; // {motion.audio.beat.decay.medium}
    this.lastBeat = 0;
    this.energy = 0;
    this.onBeat = null;
  }

  update() {
    const data = this.analyser.getFrequencyData();

    // Calculate current energy (bass-heavy)
    let currentEnergy = 0;
    const bassEnd = Math.floor(data.length * 0.1); // Focus on bass
    for (let i = 0; i < bassEnd; i++) {
      currentEnergy += data[i] / 255;
    }
    currentEnergy /= bassEnd;

    // Beat detection
    if (currentEnergy > this.energy * (1 + this.threshold)) {
      const now = performance.now();
      if (now - this.lastBeat > 100) { // Debounce
        this.lastBeat = now;
        this.onBeat?.(currentEnergy);
      }
    }

    // Decay
    this.energy = Math.max(currentEnergy, this.energy * this.decay);

    requestAnimationFrame(() => this.update());
  }
}

Scale on Beat

Framer Motion:

const BeatScale = ({ audioEngine }) => {
  const [scale, setScale] = useState(1);

  useEffect(() => {
    const beatDetector = new BeatDetector(audioEngine.analyser);

    beatDetector.onBeat = (energy) => {
      // Scale from 1 to 1.5 based on energy
      const newScale = 1 + (energy * 0.5); // {motion.audio.response.scale-max}
      setScale(newScale);

      // Decay back
      setTimeout(() => setScale(1), 100);
    };

    beatDetector.update();
  }, []);

  return (
    <motion.div
      animate={{ scale }}
      transition={{ type: 'spring', stiffness: 500, damping: 15 }}
    />
  );
};

Frequency Bars Visualizer

Canvas:

class FrequencyBars {
  constructor(canvas, audioEngine) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.audio = audioEngine;
    this.barCount = 32; // {motion.audio.visualization.bar-count.medium}
    this.sensitivity = 1; // {motion.audio.sensitivity.normal}
  }

  render() {
    const { width, height } = this.canvas;
    const data = this.audio.getFrequencyData();

    this.ctx.clearRect(0, 0, width, height);

    const barWidth = width / this.barCount;
    const step = Math.floor(data.length / this.barCount);

    for (let i = 0; i < this.barCount; i++) {
      // Average samples for this bar
      let sum = 0;
      for (let j = 0; j < step; j++) {
        sum += data[i * step + j];
      }
      const value = (sum / step / 255) * this.sensitivity;

      const barHeight = value * height;
      const x = i * barWidth;
      const y = height - barHeight;

      // Color based on frequency
      const hue = (i / this.barCount) * 120; // Green to red
      this.ctx.fillStyle = `hsl(${hue}, 80%, 50%)`;

      this.ctx.fillRect(x, y, barWidth - 2, barHeight);
    }

    requestAnimationFrame(() => this.render());
  }
}

Audio-Reactive Position

JavaScript + GSAP:

class AudioReactiveElement {
  constructor(element, audioEngine) {
    this.element = element;
    this.audio = audioEngine;
    this.positionRange = 50; // {motion.audio.response.position-range}
    this.rotationRange = 30; // {motion.audio.response.rotation-range}

    this.animate();
  }

  animate() {
    const bass = this.audio.getBass();
    const mids = this.audio.getMids();
    const highs = this.audio.getHighs();

    // Map audio to transforms
    const x = (bass - 0.5) * this.positionRange * 2;
    const y = (mids - 0.5) * this.positionRange;
    const rotation = (highs - 0.5) * this.rotationRange * 2;

    gsap.to(this.element, {
      x,
      y,
      rotation,
      duration: 0.1,
      ease: 'power1.out'
    });

    requestAnimationFrame(() => this.animate());
  }
}

Waveform Visualization

Canvas:

class Waveform {
  constructor(canvas, audioEngine) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.audio = audioEngine;
  }

  render() {
    const { width, height } = this.canvas;
    const data = this.audio.timeData;

    this.ctx.clearRect(0, 0, width, height);
    this.ctx.beginPath();
    this.ctx.moveTo(0, height / 2);

    const sliceWidth = width / data.length;

    for (let i = 0; i < data.length; i++) {
      const value = data[i] / 255;
      const y = value * height;
      const x = i * sliceWidth;

      if (i === 0) {
        this.ctx.moveTo(x, y);
      } else {
        this.ctx.lineTo(x, y);
      }
    }

    this.ctx.strokeStyle = '#00ff88';
    this.ctx.lineWidth = 2;
    this.ctx.stroke();

    requestAnimationFrame(() => this.render());
  }
}

Reduced Motion

For prefers-reduced-motion: reduce:

Full MotionReduced Alternative
Beat-synced animationStatic or gentle glow
Rapid frequency responseSlow, averaged response
Position displacementOpacity changes only

Implementation:

const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (prefersReducedMotion) {
  // Use heavy smoothing and subtle sensitivity
  analyser.smoothingTimeConstant = 0.9; // {motion.audio.smoothing.heavy}
  sensitivity = 0.5; // {motion.audio.sensitivity.subtle}

  // Use only opacity, not position
  useOpacityOnly = true;
}

Cross-References

  • See looping/ for rhythm-based loop timing
  • See particles/ for audio-reactive particle effects
  • See scale/ for scaling animations
  • See color/ for audio-reactive color changes
  • See sequencing/ for beat-timed sequences

References

  • No additional references needed for this skill

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.