Quick reference for infrastructure wiring patterns across all 6 game modes.


📦 Import Template

// Core audio
import { audioEngine } from "../audio/AudioEngine"
import { samplePackLoader } from "../audio/SamplePacks"
 
// UI components
import { ParticleCanvas, type ParticleCanvasHandle } from "../components/ParticleCanvas"
import { UnifiedControlBar } from "../components/UnifiedControlBar"
 
// Input contexts
import { useGamepad } from "../contexts/GamepadContext"
 
// Pattern utilities
import {
  createPattern,
  savePatternToLibrary,
  importPatternFromFile,
  createShareURL,
  copyToClipboard,
} from "../utils/PatternExport"

🎵 Sample Pack Integration

Available Packs

PackSamplesMode
zookick, snare, clap, hihat, chirp, bubble, bell, bassZooBeats
startwinkle, glow, shoot, chimeStarDraw
gardensprout, bloom, rustle, drop, buzzGardenGrow
oceanfish, jellyfish, turtle, octopus, bubble, whale, dolphinOceanDive
paintred, orange, yellow, green, blue, purple, whitePaintMusic
storycharacter, action, setting, happy, sad, excitingStoryTime

Loading Pattern

// In useEffect for audio init:
useEffect(() => {
  audioEngine.init().then(() => {
    const ctx = audioEngine.getContext()
    if (ctx) {
      samplePackLoader.connect(ctx)
      samplePackLoader.loadPack("zoo") // or star, garden, ocean, paint, story
    }
  })
  return () => samplePackLoader.unloadPack("zoo")
}, [midi])

Playing Samples

const playSample = (name: string, velocity = 100) => {
  const buffer = samplePackLoader.getBuffer("zoo", name)
  if (!buffer || !audioEngine.ctx) return
 
  const source = audioEngine.ctx.createBufferSource()
  source.buffer = buffer
 
  const gain = audioEngine.ctx.createGain()
  gain.gain.value = velocity / 127
 
  source.connect(gain)
  audioEngine.connectToMaster(gain)
  source.start()
}

🎆 Particle Canvas Integration

Setup

const particleRef = useRef<ParticleCanvasHandle>(null)
 
// In JSX (before UnifiedControlBar):
;<ParticleCanvas ref={particleRef} className="mode-particles" />

Emitting Particles

// Single emission
particleRef.current?.emit(x, y, count, "#4CAF50")
 
// Burst (multiple colors)
particleRef.current?.burst(x, y, ["#FFD700", "#FF6B6B", "#4ECDC4"])

Position Calculation from Grid

const emitAt = (row: number, col: number) => {
  const gridEl = gridRef.current
  const cells = gridEl?.querySelectorAll(".cell")
  const cell = cells?.[index] as HTMLElement
  if (!cell || !particleRef.current) return
 
  const rect = cell.getBoundingClientRect()
  particleRef.current.emit(rect.left + rect.width / 2, rect.top + rect.height / 2, 10, "#FFD700")
}

💾 Save/Load/Share Integration

Handler Template

// Save
const handleSave = useCallback(() => {
  const pattern = createPattern("mode", `Name ${Date.now()}`, tempo, stateData)
  savePatternToLibrary(pattern)
}, [tempo, stateData])
 
// Load
const handleLoad = useCallback(async () => {
  const pattern = await importPatternFromFile()
  if (pattern?.mode === "mode" && pattern.data) {
    // Apply pattern.data to state
  }
}, [])
 
// Share
const handleShare = useCallback(async () => {
  const pattern = createPattern("mode", "Shared", tempo, stateData)
  await copyToClipboard(createShareURL(pattern))
}, [tempo, stateData])

UnifiedControlBar Props

<UnifiedControlBar
  isPlaying={state.isPlaying}
  onPlay={togglePlay}
  onStop={handleStop}
  onClear={handleClear}
  tempo={state.tempo}
  showSaveLoad // Enable save/load buttons
  onSave={handleSave}
  onLoad={handleLoad}
  onShare={handleShare}
>
  {/* Knobs */}
</UnifiedControlBar>

📳 Gamepad Haptics Integration

Setup

const { vibrate, pulseOnBeat, supportsHaptics } = useGamepad()

Usage Patterns

// On note/pad hit
if (supportsHaptics) vibrate(60, 0.7) // 60ms, 70% intensity
 
// On beat (sequencer step)
if (supportsHaptics) pulseOnBeat() // Short 50ms pulse
 
// Intensity based on velocity
if (supportsHaptics) vibrate(50, velocity / 127)

⚠️ Critical Rules

LED_THROTTLE:
  max_flushes_per_sec: 20
  max_leds_per_flush: 10
  rule: NEVER BYPASS
 
HTTPS_REQUIRED: true # Web MIDI needs secure context
 
BLINKING_LEDS: AVOID
  - Use solid LED_COLORS only (GREEN, AMBER, RED, OFF)
  - No useLEDBeatFlash unless explicitly requested

🔧 Commands

# Type check
npx tsc --noEmit
 
# Restart service
sudo systemctl restart magic-music-land
 
# View logs
journalctl -u magic-music-land -f
 
# Test endpoint
curl -k https://0rk.de:54545

📊 Integration Status

ModeSamplePackParticlesSave/LoadHaptics
ZooBeats✅ zoo
StarDraw✅ star
GardenGrow✅ garden
OceanDive✅ ocean
PaintMusic✅ paint
StoryTime✅ story

🏗️ File Locations

src/
├── audio/
│   ├── SamplePacks.ts      # Procedural sample generation
│   └── SamplePlayer.ts     # Sample playback engine
├── components/
│   ├── ParticleCanvas.tsx  # WebGL particle system
│   └── UnifiedControlBar.tsx # Transport + save/load buttons
├── contexts/
│   └── GamepadContext.tsx  # Haptic feedback
├── utils/
│   └── PatternExport.ts    # URL/file pattern export
└── modes/
    ├── ZooBeats.tsx        # Reference implementation
    ├── StarDraw.tsx
    ├── GardenGrow.tsx
    ├── OceanDive.tsx
    ├── PaintMusic.tsx
    └── StoryTime.tsx