Skip to content

dodadoa/STFS

Repository files navigation

Spinning Top For Sound

An interactive physics simulation of spinning tops in a circular arena. Watch tops spiral toward the center, collide with realistic physics. Includes OSC (Open Sound Control) support for integration with SuperCollider, PureData, and PuckData.

Features

  • Interactive Spawning: Click anywhere in the circular arena to spawn spinning tops
  • Realistic Physics:
    • Spiral movement toward center based on spin direction
    • Elastic collisions with momentum, spin, and gravitational energy
    • Velocity decay over time
    • Smooth boundary collisions
  • Visual Feedback:
    • Direction arrows showing movement vector for each top
    • Arena-wide white strobe flash on collisions
    • Black and white minimalist design
  • Auto-Cleanup: Tops automatically disappear when they exit the arena or stop moving
  • OSC Support: Send real-time data to sound synthesis software

Getting Started

First, install dependencies:

npm install

Then, run the development server:

npm run dev

Open http://localhost:3000 with your browser to see the simulation.

How to Use

  1. Spawn Tops: Click anywhere inside the circular arena to spawn a spinning top
  2. Multiple Tops: Keep clicking to add more tops - they will interact with each other
  3. Select Top: Click on an existing top to select it and view its details (position, velocity, direction vector)
  4. Watch the Battle: Tops will spiral toward the center, collide with each other, and the arena will flash white on impacts

OSC (Open Sound Control) Integration

The simulation sends OSC messages that can be received by SuperCollider, PureData, PuckData, or any OSC-compatible software.

Configuration

Set environment variables to configure OSC (defaults to localhost on port 57120, compatible with SuperCollider and PureData):

OSC_HOST=127.0.0.1  # OSC server host (default: 127.0.0.1)
OSC_PORT=57120      # OSC server port (default: 57120)

Or create a .env.local file:

OSC_HOST=127.0.0.1
OSC_PORT=57120

Note for WSL2 users: The application automatically detects WSL2 environments and attempts to use the Windows host IP address. If automatic detection fails, manually set OSC_HOST to your Windows host IP address.

Checking OSC Status

You can check if OSC is properly configured by visiting:

http://localhost:3000/api/osc

This will return a JSON response with OSC configuration and status information.

OSC Messages

/stfs/spawn - Top Spawned

Sent when a new top is created.

Arguments:

  • x (float): Normalized X position (-1 to 1)
  • y (float): Normalized Y position (-1 to 1)

/collision - Collision Detected

Sent when tops collide (throttled to 20Hz).

Arguments:

  • intensity (float): Collision intensity (0 to 1)

/top - Individual Top Data

Sent for each top at 10Hz update rate. One message per top, sent sequentially.

Arguments:

  • index (int): Top index (0-based)
  • x (float): Normalized X position (-1 to 1)
  • y (float): Normalized Y position (-1 to 1)
  • speed (float): Normalized speed (0 to 1)
  • collisionFlash (float): Collision flash intensity (0 to 1)

/stfs/summary - Summary Data

Sent at 10Hz with overall simulation state.

Arguments:

  • topCount (int): Number of active tops
  • arenaFlash (float): Arena flash intensity (0 to 1)

SuperCollider Example

// Receive OSC messages
OSCdef(\spawn, { |msg|
    var x = msg[1], y = msg[2];
    ("Top spawned at: " ++ x ++ ", " ++ y).postln;
}, '/stfs/spawn');

OSCdef(\collision, { |msg|
    var intensity = msg[1];
    // Trigger sound on collision
    { SinOsc.ar(440 * (1 + intensity), 0, 0.1) * EnvGen.kr(Env.perc(0.01, 0.1), doneAction: 2) }.play;
}, '/collision');

OSCdef(\top, { |msg|
    var index = msg[1], x = msg[2], y = msg[3], speed = msg[4], flash = msg[5];
    // Map position to panning, speed to frequency
    var pan = x;
    var freq = 200 + (speed * 800);
    // Continuous sound based on top position and speed
}, '/top');

PureData Example

  1. Create an [udpreceive 57120] object
  2. Connect to [route /stfs /collision /top /stfs/summary] to route messages
  3. For /top messages, use [unpack i f f f f] to unpack arguments (index, x, y, speed, flash)
  4. For /stfs/spawn messages, use [unpack f f] to unpack arguments (x, y)
  5. For /collision messages, use [unpack f] to unpack intensity
  6. Map values to your synthesis parameters

PuckData Example

// In PuckData
OSC.listen(57120, (msg) => {
  if (msg.address === '/collision') {
    // Trigger sound on collision
    const intensity = msg.args[0];
    playSound(440 * (1 + intensity), intensity);
  }
  
  if (msg.address === '/top') {
    const [index, x, y, speed, flash] = msg.args;
    // Map to synthesis parameters
    setPan(x);
    setFrequency(200 + speed * 800);
    setAmplitude(flash);
  }
  
  if (msg.address === '/stfs/spawn') {
    const [x, y] = msg.args;
    // Trigger spawn sound
    playSpawnSound(x, y);
  }
  
  if (msg.address === '/stfs/summary') {
    const [topCount, arenaFlash] = msg.args;
    // Use summary data for overall mix control
    setMasterVolume(arenaFlash);
  }
});

Technology Stack

  • Next.js 16 - React framework
  • React 19 - UI library
  • Canvas API - 2D rendering
  • osc-js - OSC protocol implementation
  • Tailwind CSS - Styling

Project Structure

STFS/
├── src/
│   ├── app/
│   │   ├── page.js          # Main simulation component
│   │   ├── api/
│   │   │   └── osc/
│   │   │       └── route.js # OSC API endpoint
│   │   └── globals.css      # Global styles
│   └── lib/
│       ├── SpinningTop.js   # Physics simulation class
│       ├── renderer.js      # Canvas rendering utilities
│       └── osc.js           # OSC client utilities
├── test-udp.js              # UDP/OSC testing utility
└── package.json

License

MIT license

About

Spinning Top For Sound

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published