🎧

グラフを音楽に変える新技術: Recharts + Chart2Musicとデータソニフィケーション

に公開

Chart2Music:グラフを音楽に変えるJS/TSライブラリ

Chart2Musicは、グラフにアクセシビリティ機能を追加できるTypeScript/JavaScriptパッケージです。データソニフィケーション(データ可聴化)、スクリーンリーダー対応、キーボード操作に対応し、既存のチャートライブラリと組み合わせて使用できます。

https://www.chart2music.com/docs/

この記事では、Chart2Music(TypeScript/JavaScript製のアクセシビリティライブラリ)を使って、Rechartsで作成したグラフを「聴ける」ものに変換する方法を、実装例とともに紹介します。

東海道新幹線の「音の旅」を作ってみよう

では、Recharts + Chart2Musicのサンプル作成のため、「東海道新幹線(東京-新大阪)が通過する全市区町村の人口データ」を自作し、データ可視化とデータ可聴化を実装してみました。実装したデモサイトも作成してみましたので、実際に体験してもらえるとChart2Musicの素晴らしさが伝わるかと思います。

デモサイト画面
Recharts+Chart2Musicのサンプルデモサイトの画面

サンプルデモサイト

https://chart2music-sample.vercel.app/

データ

export const shinkansenAllData: ShinkansenData[] = [
  { id: 1, "駅・区間": "東京駅", 市区町村: "千代田区", 人口: 66680 },
  { id: 2, "駅・区間": "品川駅", 市区町村: "港区", 人口: 260486 },
  { id: 3, "駅・区間": "品川-新横浜", 市区町村: "品川区", 人口: 422488 },
  { id: 4, "駅・区間": "品川-新横浜", 市区町村: "大田区", 人口: 748081 },
  { id: 5, "駅・区間": "品川-新横浜", 市区町村: "川崎市中原区", 人口: 263683 },
  { id: 6, "駅・区間": "新横浜駅", 市区町村: "横浜市港北区", 人口: 358530 },
  ...
]

東海道新幹線(東京-新大阪)が通過する全市区町村の人口データ

一見地味でランダムパターンのデータです。当然ながら、可視化しても特徴的なパターンは見出せません。

しかし、音で聴くことで全く新しい価値が生まれると確信します。

キーボードの矢印キーでデータを移動していく体験は、まるで新幹線の車窓から景色の移ろいを感じるかのような、映画的な体験になります。

開発環境

まずは、開発環境の準備から始めます。このハンズオンでは、Viteを使ってReact + TypeScriptのプロジェクトをセットアップします。

フレームワーク: Vite + React (TypeScript)
グラフライブラリ: Recharts
可聴化: Chart2Music
パッケージ管理: pnpm

ターミナルを開き、以下のコマンドを実行してください。

# 1. viteプロジェクトのセットアップ(プロジェクト名は c2m-minimum-example)  
pnpm create vite c2m-minimum-example --template react-ts

# 2. 作成したプロジェクトのディレクトリに移動  
cd c2m-minimum-example

# 3. 必要なライブラリ(recharts と chart2music)をインストール  
pnpm add recharts chart2music

ディレクトリ構成

開発を進めやすくするために、以下のようなディレクトリ構成を想定します。
components、data、types フォルダを /src の中に作成しておきましょう。

/src  
├── App.tsx  
├── components # コンポーネントを格納するフォルダ  
│   └── charts  
│       └── PopulationBarChart.tsx # これから作成するグラフコンポーネント  
├── data # グラフで使用するデータを格納するフォルダ  
│   └── shinkansenAll.ts  
├── index.css  
├── main.tsx  
├── types # 型定義ファイルを格納するフォルダ  
│   └── chart2music.d.ts # これから作成する型定義ファイル  
└── ....

Step 1: 基本的なRechartsチャートの作成

まずは、可聴化する前の土台となる、通常のRechartsを使った棒グラフを作成します。

src/components/charts/PopulationBarChart.tsx というファイルを作成し、以下のコードを貼り付けてください。

import React from 'react';  
import {  
  BarChart,  
  Bar,  
  XAxis,  
  YAxis,  
  CartesianGrid,  
  Tooltip,  
  ResponsiveContainer,  
} from 'recharts';  
import { shinkansenAllData } from '../../data/shinkansenAll'; // データは別途用意してください

const PopulationBarChart: React.FC = () => {  
  return (  
    <div className="w-full">  
      <h2 className="text-2xl font-bold mb-4 text-center">  
        新幹線沿線市区町村人口(東京→新大阪)  
      </h2>  
        
      <div style={{ width: '100%', height: '400px' }}>  
        <ResponsiveContainer width="100%" height="100%">  
          <BarChart  
            data={shinkansenAllData}  
            margin={{  
              top: 20,  
              right: 30,  
              left: 20,  
              bottom: 80,  
            }}  
          >  
            <CartesianGrid strokeDasharray="3 3" />  
            <XAxis   
              dataKey="市区町村"  
              angle={-45}  
              textAnchor="end"  
              height={100}  
            />  
            <YAxis   
              tickFormatter={(value) => `${(value / 10000).toFixed(0)}`}  
              fontSize={12}  
            />  
            <Tooltip   
              formatter={(value) => [value.toLocaleString() + '人', '人口']}  
              labelStyle={{ color: '#000' }}  
            />  
            <Bar dataKey="人口" fill="#8884d8" />  
          </BarChart>  
        </ResponsiveContainer>  
      </div>  
    </div>  
  );  
};

export default PopulationBarChart;

作成したコンポーネントを App.tsx などで読み込んで表示します。

import './App.css'
import PopulationBarChart from './components/charts/PopulationBarChart'

function App() {

  return (
    <div className="App">
      // 作成したチャートコンポーネントを呼び出し
      <PopulationBarChart />
    </div>

  )
}

export default App

下のようなグラフが表示されることを確認しましょう。
RechartsでBarChartを描画
RechartsでBarChartを描画

重要なポイント

Step 2: Chart2Musicで可聴化する

ここからが本番です。Chart2Musicをインテグレーションすることで、グラフが「聴ける」ように変わります。

ただし、Chart2Musicは、現時点で公式のTypeScript型定義ファイルを提供していません。

そのため、TypeScriptプロジェクトで快適に利用するために、自分で簡単な型定義ファイルを作成します。これにより、エディタのコード補完が効くようになり、意図しないプロパティ名を指定してしまうといったミスを防げます。

/src/types/chart2music.d.ts を作成し、以下の内容を記述してください。

declare module "chart2music" {  
  interface C2MChartOptions {  
    title: string;  
    type: "bar" | "line" | "pie";  
    element: HTMLElement;  
    cc?: HTMLElement;  
    axes: {  
      x: {  
        label: string;  
        format: (index: number) => string;  
      };  
      y: {  
        label: string;  
        format: (value: number) => string;  
      };  
    };  
    data: number[];  
  }

  export function c2mChart(options: C2MChartOptions): any;  
}

次に先ほど作成したRechartsコンポーネントにChart2Musicを統合していきます。
ここでのポイントは、「DOM要素の参照」、「データの変換」、「Chart2Musicの初期化」の3つです。

1. DOM要素を参照するためのRefを追加

Chart2Musicは、「どのグラフ」を「どこにキャプションを表示して」可聴化するのかを知る必要があります。
そのために、Reactの useRef フックを使って、対象となるHTML要素を直接参照できるようにします。
コンポーネントの先頭に、以下の2つのrefを追加します。

const chartRef = useRef<HTMLDivElement>(null); // グラフのコンテナ要素用  
const ccRef = useRef<HTMLDivElement>(null);   // 音声読み上げ内容のキャプション表示用

2. Chart2Music用のデータに変換

Rechartsは [{市区町村: '東京', 人口: 1000}, ...] のようなオブジェクトの配列をデータとして扱いますが、Chart2Musicは [1000, 2000, ...] のような単純な数値の配列を期待します。
そのため、元のデータから人口データだけを抜き出した新しい配列を作成します。

// Chart2Musicは数値の配列をデータとして受け取る  
const c2mData = shinkansenAllData.map(item => item.人口);

3. useEffectでChart2Musicを初期化

コンポーネントが画面にレンダリングされた後に、Chart2Musicの初期化処理を実行します。これには useEffect フックを使用します。

初期化処理では、c2mChart関数にオプションオブジェクトを渡します。

useEffect(() => {  
  // refの要素が実際にDOMに描画された後に処理を実行  
  if (chartRef.current && ccRef.current) {  
    c2mChart({  
      type: "bar",  
      element: chartRef.current,  
      cc: ccRef.current,  
      data: c2mData,  
      title: "新幹線沿線市区町村人口",  
      axes: {  
        x: {  
          label: "市区町村",  
          format: (index) => shinkansenAllData[index].市区町村  
        },  
        y: {  
          label: "人口",  
          format: (val) => `${Math.round(val / 10000)}万人`  
        }  
      }  
    });  
  }  
}, [c2mData]); // c2mDataが変更された時のみ再実行

全体コード

ここまでの修正を反映した PopulationBarChart.tsx の全体像はこちらです。
refをdiv要素に設定するのを忘れないようにしましょう。

import React, { useRef, useEffect } from 'react';  
import {  
  BarChart,  
  Bar,  
  XAxis,  
  YAxis,  
  CartesianGrid,  
  Tooltip,  
  ResponsiveContainer,  
} from 'recharts';  
import { shinkansenAllData } from '../../data/shinkansenAll';  
import { c2mChart } from 'chart2music';

const PopulationBarChart: React.FC = () => {  
  // Chart2Music用のref  
  const chartRef = useRef<HTMLDivElement>(null);  
  const ccRef = useRef<HTMLDivElement>(null);

  // Chart2Music用のデータ変換  
  const c2mData = shinkansenAllData.map(item => item.人口);

  // Chart2Music初期化  
  useEffect(() => {  
    if (chartRef.current && ccRef.current) {  
      c2mChart({  
        type: "bar",  
        element: chartRef.current,  
        cc: ccRef.current,  
        data: c2mData,  
        title: "新幹線沿線市区町村人口",  
        axes: {  
          x: {  
            label: "市区町村",  
            format: (index) => shinkansenAllData[index].市区町村  
          },  
          y: {  
            label: "人口",  
            format: (val) => `${Math.round(val / 10000)}万人`  
          }  
        }  
      });  
    }  
  }, [c2mData]);

  return (  
    <div className="w-full">  
      <h2 className="text-2xl font-bold mb-4 text-center">  
        新幹線沿線市区町村人口(東京→新大阪)  
      </h2>  
        
      {/* Chart2Music統合のため、グラフのコンテナにrefを追加 */}  
      <div ref={chartRef} style={{ width: '100%', height: '400px' }}>  
        <ResponsiveContainer width="100%" height="100%">  
          <BarChart  
            data={shinkansenAllData}  
            margin={{  
              top: 20,  
              right: 30,  
              left: 20,  
              bottom: 80,  
            }}  
          >  
            <CartesianGrid strokeDasharray="3 3" />  
            <XAxis   
              dataKey="市区町村"  
              angle={-45}  
              textAnchor="end"  
              height={100}  
            />  
            <YAxis   
              tickFormatter={(value) => `${(value / 10000).toFixed(0)}`}  
              fontSize={12}  
            />  
            <Tooltip   
              formatter={(value) => [value.toLocaleString() + '人', '人口']}  
              labelStyle={{ color: '#000' }}  
            />  
            <Bar dataKey="人口" fill="#8884d8" />  
          </BarChart>  
        </ResponsiveContainer>  
      </div>

      {/* Chart2Music用のクローズドキャプション領域(ここに読み上げ内容が表示される) */}  
      <div ref={ccRef}></div>  
    </div>  
  );  
};

export default PopulationBarChart;

これだけです! useEffectとc2mChart関数の呼び出しを追加するだけで、あなたのグラフは「聴ける」グラフに生まれ変わりました。

Chart2Musicの使い方を試してみよう

これで実装は完了です!

ブラウザで画面を表示し、以下のキーボード操作を試してみてください。

  1. まず、Tabキー を何度か押して、グラフにフォーカスを合わせます。(グラフの周りにアウトラインが表示されます)
  2. スペースキー を押すと、データの最初から最後まで、値の高さに応じた音(ピッチ)で再生されます。人口が多い市区町村は高い音、少ないところは低い音が鳴るのがわかるはずです。
  3. ← →(左右の矢印キー) を使うと、データポイントを一つずつ移動できます。移動するたびに「(市区町村名)、(人口)」のように、具体的なデータが読み上げられます。
  4. Shitキー + function(fn)キー + ← →(左右の矢印キー) を使うと、選択要素から右端、左端までを通して再生できます
  5. Escキー でいつでも再生を停止できます。
  6. Hキー で操作方法を呼び出せます

トラブルシューティング

もしうまく動作しない場合は、以下の点を確認してみてください。

  1. チャートが表示されない
    • <ResponsiveContainer> の親要素に、CSSで height が明示的に指定されているか確認してください
    • コンポーネントに渡している data 配列や、dataKey のプロパティ名が正しいか確認してください
  2. 音が出ない / キーボード操作が効かない
    • ブラウザがアクティブな状態(ページが選択されている状態)か確認してください
    • ブラウザの開発者ツールを開き、コンソールに c2mChart に関するエラーが出ていないか確認してください
    • chartRef と ccRef が、対応するdiv要素に正しく設定されているか確認してください
  3. 型エラーが発生する (TypeScript)
    • src/types/chart2music.d.ts ファイルが正しい場所に配置されているか確認してください
    • tsconfig.json が正しく設定されているか(特にincludeにsrcが含まれているか)確認してください

Chart2Musicの哲学

Chart2Musicは、視覚障害者がデータを聞き取れるようにすることを目的にしており、開発チームには、設計、開発、テストの各段階に視覚障害者が参加しているようです。まさしく、「Nothing About Us, Without Us(私たちのことを、私たち抜きに決めるな)」というアクセシビリティ分野の重要な原則を体現したライブラリといえます。

最も素晴らしい点は、RechartsやChart.jsなどで描画されたSVGグラフに、後から機能を追加できる手軽さです。既存のプロジェクトを大幅に変更することなく、アクセシビリティを向上させることができます。

  • 包括的な設計- C2M の開発チームには、設計、開発、テストの各段階に視覚障碍者も参加しています。
  • スクリーン リーダー ユーザー向けのアクセシビリティ- 視覚障碍のあるユーザーが、音声と探索機能を使用してチャートやグラフに関する情報をすばやく理解できるようにします。
  • アクセシビリティ要件のメンテナンスが容易- 従来、チャートやグラフのアクセシビリティは、生成やメンテナンスが難しい代替テキストや、エンドユーザーエクスペリエンスを低下させるデータテーブルを使用して実現されていました。C2Mは、ユーザーエクスペリエンスを向上させながら、自動化されたソリューションを提供します。
  • ビジュアルに依存しない- ビジュアルの作成方法に関係なく、C2M をチャートと一緒に使用できます。ChartJS ですか? D3.js ですか? 画像ですか? C2M はビジュアルと並行して動作します。
  • どこでも無料で使用可能- C2M はMIT ライセンスなので、個人、商用、政府など、あらゆるものに統合できます。

Chart2Music Introduction Key features より

「代替」ではなく「拡張」

従来のアクセシビリティ対応は「視覚の代替」という考え方が主流でした。

Chart2Musicは、「聴覚による体験の拡張」というコンセプトのもと、音でデータの全体像を「感じ」、スクリーンリーダーの詳細情報で「理解する」という、2層構造の豊かな体験を提供しています。

データ可視化の"洞察"は全ての人に届いているのか?

多くの場合、Webアクセシビリティの多くはボタンやフォームなど「UI要素」に焦点が当てられます。しかし、データ可視化の分野ではどうでしょう。棒グラフ、折れ線グラフ、散布図...それらから得られる"洞察"は、すべての人に等しく届いているのでしょうか?

従来のアプローチでは、スクリーンリーダーが「東京都の人口は1400万人、神奈川県の人口は918万人...」と機械的に読み上げるのが精一杯でした。つまり、情報は届けられるが、データの「うねり」や「パターン」、そして何より「驚き」を感じるに至っていません。

「データを音にする」— この革新的なアプローチが、その可能性を気づかせてくれます。

データソニフィケーションとは?

データソニフィケーションとは、非音声の音響を使ってデータや情報を伝達する技術です。

データ可視化が、データの値を「形・位置・大きさ・色」といった視覚要素(グリフ)にマッピングするのに対し、データソニフィケーションは「音の高さ・大きさ・音色・リズム」といった聴覚要素にマッピングする手法といえます。

データソニフィケーション入門: 準備編

活用例

一見新しく聞こえるかもしれませんが、実は私たちの身の回りに既に存在しています。

最も古典的な例がガイガーカウンターです。1920年代に完成したこの装置は、放射線量を「カチカチ」という音の頻度で知らせます。目に見えない放射線を、聴覚を通じて直感的に理解できる画期的な発明だったようです。

このような可聴化は、心拍数モニター(心拍を音で表現)、パルスオキシメーター(血中酸素濃度を音の調子で表現)など、医療分野を中心に応用が広がっていますね。

人間の聴覚は、ノイズの中から特定のパターンを見つけ出す能力に長けていると言います。この特性を活かし、視覚だけでは気づきにくいデータの異常やトレンドを発見できると、ますます世界が広がりますね!

おわりに:データが奏でる新しい世界へ

データの新しい次元

今回は一次元的な棒グラフを扱いましたが、散布図や時系列データの可聴化はどのように実装すべきでしょうか? 2つの変数の相関を音でどう表現するのが最も直感的でしょうか?

こうした探求により、データとの全く新しい関わり方が生まれつつあります。

その価値にはアクセシビリティだけに留まらず、大きな可能性とフロンティアが広がっていますね。

  • 多感覚学習: 聴覚と視覚の両方を使うことで、理解が深まる
  • パターン発見: 人間の聴覚は、視覚では気づきにくい異常やトレンドを捉えられる
  • 情報の民主化: 専門的なグラフの読み方を知らなくても、データの「感じ」を掴める

私は、Chart2Musicを使って初めてグラフから音が出た瞬間、データが単なる数値を超えて「生きた体験」に変わる瞬間を目の当たりにした感動を覚えました。

Chart2Musicは現在バージョン1.19.0がリリースされており、継続的に開発が行われているようです。チャートにアクセシビリティ機能を追加できるこのライブラリは、データ探索やデータ可視化などに取り組む開発者が知っておくべき強力なツールと言えそうです。

未来のスタンダードへ

Webアクセシビリティは「誰かのため」の特別な配慮ではありません。すべての人がより豊かにデータと向き合える「未来のスタンダード」でしょう。その結果、データの新しい"聴き方"は新しい発見を導き、きっと新しい未来の扉を開くと思うとワクワクしますね。

ぜひ、あなたのプロジェクトでも「聴けるグラフ」を実装してみてください。データが奏でる新しい世界が、きっとあなたを待っています。


リソース

参考文献

Discussion