UTURI

Transform Data into Sound

Data visualization charts cannot provide the same experience to non-visual users.
Now use @uturi/sonification to quickly and easily convey data changes through sound.

Interactive Demo

Getting Started

Installation

Global Installation

npm install @uturi/sonification
# or
yarn add @uturi/sonification
# or
pnpm add @uturi/sonification

Framework-Specific Installation (RECOMMENDED)

// you can also install the particular framework version
npm install @uturi/sonification/react
# or
yarn add @uturi/sonification/react
# or
pnpm add @uturi/sonification/react

Quick Start

Vanilla JavaScript / TypeScript

import { Sonifier } from '@uturi/sonification';

const salesData = [100, 150, 80, 200, 175, 300];
const sonifier = new Sonifier();
sonifier.sonify(salesData, 'frequency', { autoPlay: true });

React

import { useSonifier } from '@uturi/sonification/react';
import { useCallback } from 'react';

function ChartWithSound() {
  const chartData = [10, 25, 15, 40, 35, 60];
  
  const { sonify, isPlaying, error, result } = useSonifier({
    duration: 2.0,
    volume: 0.5,
  });

  const handlePlaySound = async () => {
    try {
      await sonify(chartData, 'melody', { autoPlay: true });
    } catch (err) {
      console.error('Error:', err);
    }
  };

  return (
    <div>
      <button onClick={handlePlaySound} disabled={isPlaying}>
        {isPlaying ? 'Playing...' : 'Play Chart Sound'}
      </button>
      {error && <div>Error: {error.message}</div>}
    </div>
  );
}

Vue

import { useSonifier } from '@uturi/sonification/vue';

const chartData = [10, 25, 15, 40, 35, 60];
const { sonify, isPlaying, error, result } = useSonifier({
  duration: 2.0,
  volume: 0.5,
});

const handlePlaySound = async () => {
  try {
    await sonify(chartData, 'volume', { autoPlay: true });
  } catch (err) {
    console.error('Error:', err);
  }
};

<template>
  <div>
    <button @click="handlePlaySound" :disabled="isPlaying">
      {{ isPlaying ? 'Playing...' : 'Play Chart Sound' }}
    </button>
    <div v-if="error">Error: {{ error.message }}</div>
  </div>
</template>

Svelte

import { useSonifier } from '@uturi/sonification/svelte';

const chartData = [10, 25, 15, 40, 35, 60];
const { sonify, isPlaying, error, result } = useSonifier({
  duration: 2.0,
  volume: 0.5,
});

const handlePlaySound = async () => {
  try {
    await sonify(chartData, 'frequency', { autoPlay: true });
  } catch (err) {
    console.error('Error:', err);
  }
};

<button on:click={handlePlaySound} disabled={$isPlaying}>
  {$isPlaying ? 'Playing...' : 'Play Chart Sound'}
</button>
{#if $error}
  <div>Error: {$error.message}</div>
{/if}

Sonification Methods

1. Frequency - Pitch changes according to value

// Frequency: Pitch changes according to value
// Higher values produce higher pitches
const sonifier = new Sonifier();
const result = await sonifier.sonify(data, 'frequency', { autoPlay: true });

2. Volume - Volume changes according to value

// Volume: Volume changes according to value
// Higher values produce louder sounds
const sonifier = new Sonifier();
const result = await sonifier.sonify(data, 'volume', { autoPlay: true });

3. Rhythm - Rhythm pattern changes according to value

// Rhythm: Rhythm pattern changes according to value
// Higher values produce faster rhythms
const sonifier = new Sonifier();
const result = await sonifier.sonify(data, 'rhythm', { autoPlay: true });

4. Melody - Musical scale using notes (C, D, E, F, G, A, B)

// Melody: Scale changes according to value
// Creates a musical melody using notes (C, D, E, F, G, A, B)
const sonifier = new Sonifier();
const result = await sonifier.sonify(data, 'melody', { autoPlay: true });

Waveform Types

Notice

When choosing a Waveform Type, carefully consider both the acoustic characteristics of the sound and the nature of the data you want to convey. Each waveform has distinct auditory properties, and the appropriate waveform varies depending on the data characteristics.

WaveformCharacteristicsChange Detection AccuracyAuditory FatigueSuitable Use Cases
SineSmooth and pure tone★★★★★★★★★★Common values, key data with slopes
SquareStrong attack, abrupt transitions★★★☆☆★★☆☆☆Outliers, warnings, state transitions
SawtoothClear rise/fall, strong harmonics★★☆☆☆★★☆☆☆Enhanced slope/trend recognition

Choose from three waveform types

// Three waveform types available
const sonifier = new Sonifier({
  waveType: 'sine',     // default
  // waveType: 'square',
  // waveType: 'sawtooth',
});

// Change waveform dynamically
sonifier.setConfig({
  waveType: 'square',  // Switch to square wave
});

await sonifier.sonify(data, 'frequency', { autoPlay: true });

Custom Configuration

Initial Configuration

// Fine-tuned configuration customization
const sonifier = new Sonifier({
  // Basic audio settings
  duration: 3.0,        // 3 seconds playback
  sampleRate: 44100,    // CD quality
  waveType: 'square',   // Waveform type: 'sine' | 'square' | 'sawtooth'
  
  // Frequency range (Hz)
  minFrequency: 200,    // Lowest pitch
  maxFrequency: 800,    // Highest pitch
  
  // Volume range (0-1)
  minVolume: 0.1,       // Minimum volume
  maxVolume: 0.8,       // Maximum volume
  
  // Rhythm range (0-1)
  minRhythm: 0.2,       // Minimum rhythm
  maxRhythm: 0.9,       // Maximum rhythm
});

await sonifier.sonify(salesData, 'frequency', { autoPlay: true });

Dynamic Configuration Update

// Update configuration dynamically
const sonifier = new Sonifier({
  duration: 2.0,
  volume: 0.3,
  waveType: 'sine',
});

// Later, update the configuration
sonifier.setConfig({
  duration: 4.0,
  volume: 0.6,
  waveType: 'square',  // Change waveform type
});

// Get current configuration
const currentConfig = sonifier.getConfig();
console.log('Current config:', currentConfig);

Manual Audio Playback

Generate audio and play later

// Generate audio without auto-playing
const sonifier = new Sonifier();
const result = await sonifier.sonify(data, 'melody', { autoPlay: false });

// Play later
await sonifier.play(result.audioBuffer);

// Or use with Web Audio API directly
const audioContext = new AudioContext();
const source = audioContext.createBufferSource();
source.buffer = result.audioBuffer;
source.connect(audioContext.destination);
source.start();

Error Handling

SonificationError Class

// SonificationError Class
import { SonificationError, ERROR_CODES } from '@uturi/sonification';

// All errors thrown by the library are instances of SonificationError
class SonificationError extends Error {
  readonly code: SonificationErrorCode;  // Error code to distinguish error types
  readonly cause?: Error;                 // Original error (if any)
  readonly field?: string;                // Field name (for validation errors)
}

// Error Codes
export const ERROR_CODES = {
  WORKER_ERROR: 'WORKER_ERROR',           // Web Worker initialization or execution error
  VALIDATION_ERROR: 'VALIDATION_ERROR',   // Input data or configuration validation failed
  TIMEOUT_ERROR: 'TIMEOUT_ERROR',         // Audio generation timeout
  AUDIO_CONTEXT_ERROR: 'AUDIO_CONTEXT_ERROR', // AudioContext related error
  UNKNOWN_ERROR: 'UNKNOWN_ERROR',         // Unknown error
} as const;

Basic Error Handling

import { Sonifier, SonificationError, ERROR_CODES } from '@uturi/sonification';

const sonifier = new Sonifier();

try {
  const result = await sonifier.sonify(data, 'melody', { autoPlay: true });
  console.log('Success:', result);
} catch (error) {
  if (error instanceof SonificationError) {
    switch (error.code) {
      case ERROR_CODES.VALIDATION_ERROR:
        console.error('Validation error:', error.message);
        console.error('Field:', error.field);
        break;
      case ERROR_CODES.WORKER_ERROR:
        console.error('Worker error:', error.message);
        break;
      case ERROR_CODES.TIMEOUT_ERROR:
        console.error('Timeout error:', error.message);
        break;
      case ERROR_CODES.AUDIO_CONTEXT_ERROR:
        console.error('AudioContext error:', error.message);
        break;
      default:
        console.error('Unknown error:', error.message);
    }
  }
}