Mini Tokyo 3D バージョン 2.4.0 リリース

Mini Tokyo 3D バージョン 2.4.0 がリリースされました (GitHub)。2.3.0 からの追加・修正機能を見ていきましょう。

JR成田線我孫子支線が全線開通

成田線我孫子支線我孫子〜成田間全線開通しました。常磐線快速列車の一部が我孫子から成田まで乗り入れています。単線なので途中の駅で列車交換(対向列車の待ち合わせ)を行いますが、成田線我孫子支線では全駅で列車交換が可能です。

駅名検索機能の改善

駅名検索フィールドのオートコンプリートの候補リストにおけるローマ字の長音記号(例: Ō)や副駅名(例: 押上〈スカイツリー前〉)などの表記の揺れを吸収し、さらに同名表記で位置の異なる駅(例: 小川町 (東京)と小川町 (埼玉))を区別するようにしました。

再生モードでの旅客機の表示

これまで再生モードに対応していたのは列車の運行だけでしたが、羽田・成田発着旅客機の運行にも対応しました。ただし、当日発着便に限ります。任意の時刻・再生スピードで旅客機の動きを見ることができます。

下の動画は羽田空港周辺の100倍速再生の様子ですが、7時台は羽田から離陸する分が多いものの、8時台になると各地を早朝に離陸した便が集まってくるのがわかります。

駅出口情報の拡充

銀座線に加えて、丸の内線、日比谷線東西線、千代田線、有楽町線半蔵門線南北線副都心線各駅の出口情報を追加しました。

高尾発着成田エクスプレス、八王子発着むさしの号、千代田線乗り入れ小田急ロマンスカーに対応

高尾発着成田エクスプレスに対応。なぜか東京公共交通オープンデータチャレンジ提供データには中央線を走る特急列車のデータが含まれていないので、手動で入力しました。

また、武蔵野線経由で大宮と八王子を結ぶむさしの号も、国立〜八王子間が表示されていませんでしたが、全線運転を開始しました。

さらに、小田急ロマンスカーのうち、千代田線に乗り入れるメトロはこね・メトロモーニングウェイ・メトロホームウェイの表参道〜成城学園前新百合ヶ丘・町田間が表示されていなかった問題を解消しました。

可視領域の厳密な計算による描画性能向上

画面に表示されない列車や旅客機などの3Dオブジェクトは毎フレーム位置計算を行う必要はなく、計算を省略することでパフォーマンスを最適化することができます。これまではマップの可視領域を含む緯線・経線に沿った矩形を使い、3Dオブジェクト位置計算の必要性を簡易的に判定していましたが、厳密な可視領域の台形を使うように切り替えました。実際にはスムーズなアニメーションのために若干のバッファ領域も入れています。これにより、10%程度描画性能が向上しました。

f:id:nagixx:20200911174046p:plain

JR特急列車の追加

房総方面の特急しおさいわかしお・さざなみで使用される255系、E257系500番台に対応。

これに合わせて、総武快速線の列車のうち千葉以東で成田線総武本線に乗り入れる快速列車の一部が表示されなかったり、列車番号が切り替わっていなかった問題を修正しました。また、佐倉駅での連結・切り離しを行う列車にも対応しました。

モード取得・変更 API、列車・旅客機追跡 API の追加

バージョン 2.4.0 では、モードの取得・変更を行うメソッド、追跡する列車 ID・旅客機 ID の取得・設定を行うメソッドが追加されました。

メソッド 説明
getClockMode 現在のクロックモードを返す
getSelection 追跡中の列車またはフライトの ID を返す
getTrackingMode 現在の追跡モードを返す
getViewMode 現在のビューモードを返す
setClockMode クロックモードを設定する
setSelection 追跡する列車またはフライトの ID を設定する
setTrackingMode 追跡モードを設定する
setViewMode ビューモードを設定する

詳細は Mini Tokyo 3D 開発者ガイドをご覧ください。

Mini Tokyo 3D バージョン 2.3.0 リリース

Mini Tokyo 3D バージョン 2.3.0 がリリースされました (GitHub)。2.2.0 からの追加・修正機能を見ていきましょう。

JR八高線の八王子〜小川町間、川越線の川越〜高麗川間が開通

八王子と高崎を結ぶJR八高線の、八王子から東武東上線と接続する小川町までが開通しました。同時に、大宮〜川越間が開通していたJR川越線高麗川まで延伸して全線が開通しました。

八高線高麗川川越線に接続しており、八王子〜高麗川〜川越は電化しているため、この区間は直通運転が行われています。一方、八高線高麗川以北は非電化区間のため、運行は独立しています。両線とも単線なので、途中の駅で列車交換(対向列車の待ち合わせ)を行う、ピタゴラスイッチ的な動きの様子が見られます。

駅選択時のハイライト表示

駅にマウスポインタを合わせた時に、ポップアップ表示と共に駅の輪郭をハイライトして注目している場所がわかりやすくなりました。

駅出口情報の表示

駅をクリックまたはタップして選択すると、ズームインして駅出口の場所を表示するようになりました。ぞれぞれ地図上に出口番号が出るほか、出口の一覧も表示されます。一覧の中で出口の名前にマウスポインタを合わせたり、タップをすると地図上の出口の名前がハイライトされ、場所の確認をすることができます。

地上モードに切り替えるとビルや歩道との対応もわかりやすいです。まずは銀座線の全駅に対応。順次、地下鉄の駅をカバーする予定です。

マップスタイルの更新による道路・地下街の詳細表示

Mapboxのマップスタイルを更新して、ズームレベル15以上で道路と地下街の詳細なポリゴンデータが表示されるようになりました。この機能は、日本独自のゼンリンの地図データがベースになっているもので、しばらく前からデータは提供されていたのですが、ようやく Mini Tokyo 3D でも対応ができました。

右がこれまでのマップスタイルで、左が新しくなったマップスタイルです。道路や歩道が正確な幅で描かれ、中央分離帯の緑や日比谷公園の遊歩道も見分けられるようになっているのがおわかりいただけると思います。

f:id:nagixx:20200815204445p:plain

地下表示モードでは、下のように大手町〜東京〜銀座の地下街が大きく広がりを持っているのがわかります。

f:id:nagixx:20200815204547p:plain

花火レイヤーの追加

3D マップ上で花火大会を再現しました。7/23〜26の4連休と8/8〜10の3連休で、隅田川花火大会、足立の花火、幕張ビーチ花火フェスタ、みなとみらいスマートフェスティバル、神宮外苑花火大会江戸川花火大会、TOKYOいたばし花火フェスティバル/戸田橋花火大会をバーチャル開催。再生モードで時間をさかのぼれば、花火の打ち上げを見ることができますよ。

JR特急列車の追加

特急サフィール踊り子E261系、特急踊り子E259系2000番台、寝台特急サンライズ出雲サンライズ瀬戸285系、特急ひたち・ときわE657系、特急スワローあかぎ・あかぎ・草津651系に対応。

イベントリスナAPIの追加

バージョン 2.3.0 では MiniTokyo3D クラスにイベントリスナの追加・削除を行う次のメソッドが追加されました。

メソッド 説明
off イベントリスナを削除
on イベントリスナを追加
once 一度だけ呼び出されるリスナを追加

上記のメソッドで指定するイベントリスナを使用して、マップで発生した次のイベントを受け取ることができます。

イベント 説明
boxzoomcancel 「ボックスズーム」操作がキャンセル
boxzoomend 「ボックスズーム」操作が終了
boxzoomstart 「ボックスズーム」操作が開始
click ポインティングデバイを押して離す
contextmenu マウスの右ボタンがクリック
dblclick ポインティングデバイスを2回連続して押して離す
drag 「移動のためのドラッグ」操作中
dragend 「移動のためのドラッグ」操作が終了
dragstart 「移動のためのドラッグ」操作が開始
error エラーが発生
load 必要なリソースのダウンロード・表示が完了
mousedown ポインティングデバイスが押される
mousemove ポインティングデバイスが移動
mouseover カーソルがマップまたは子要素に入る
mouseup ポインティングデバイスが離された
move あるビューから別のビューへのアニメーション遷移中
moveend あるビューから別のビューへの遷移を完了
movestart あるビューから別のビューに遷移する直前
pitch 傾きの状態遷移アニメーションの間
pitchend 傾きが変化し終わった直後
pitchstart 傾きが変化し始める直前
resize マップのサイズが変更
rotate 「回転のためのドラッグ」操作中
rotateend 「回転のためのドラッグ」操作が終了
rotatestart 「回転のためのドラッグ」操作が開始
touchcancel touchcancel イベントが発生
touchend touchend イベントが発生
touchmove touchmove イベントが発生
touchstart touchstart イベントが発生
wheel wheel イベントが発生
zoom あるズームレベルから別のズームレベルへのアニメーション遷移中
zoomend あるズームレベルから別のズームレベルへの移行を完了した直後
zoomstart あるズームレベルから別のズームレベルへの移行を開始する直前

詳細は Mini Tokyo 3D 開発者ガイドをご覧ください。

Mini Tokyo 3D バージョン 2.2.0 リリース

Mini Tokyo 3D バージョン 2.2.0 がリリースされました (GitHub)。2.1.0 からの追加・修正機能を見ていきましょう。

東武伊勢崎線東武動物公園〜久喜間、千葉モノレールが全線開通

東武伊勢崎線は、浅草から東武動物公園までの「東武スカイツリーライン」と路線愛称が付いている区間は開通していたものの、JR 宇都宮線との接続駅である久喜までは達していませんでした。今回、2駅分ですが Mini Tokyo 3D マップの北の端である久喜まで開通し、特急りょうもうなどの伊勢崎方面の列車が走り始めました。

また、千葉モノレール 1号線の千葉みなと〜県庁前間、2号線の千葉〜千城台間が全線開通しました。千葉モノレールは長らく稲毛海岸方面や青葉病院への延伸計画があったのですが、こちらは採算性の懸念から昨年に延伸中止が決定されたみたいですね。

東武東上線・小川町、小田急小田原線伊勢原まで延伸

東武東上線はこれまで川越市止まりだった路線が、JR 八高線との接続駅である小川町まで延伸しました(八高線はまだ未通ですが)。

小田急小田原線は、本厚木止まりだった路線が伊勢原まで延伸しました。

駅詳細情報のポップアップ

駅にマウスポインタを合わせる(タッチデバイスの場合はタッチする)と駅のサムネイル画像と路線名がポップアップで表示されるようになりました。現在、乗り換え駅の重複を含めて 2,087 駅あるのですが、すべての駅に対応しています。初めて行く場所でもなんとなく街の雰囲気がわかりますね。

列車・フライトのシェア

追跡中の列車・フライトのシェア機能を追加しました。「この列車をシェア」ボタンを押して SNS やメール、AirDrop 等を選ぶと相手 に URL が送信され、開くと同じ列車を追跡した状態で見られます。相手側でアプリをインストールすることなく、ブラウザだけで見ることができるので便利です。待ち合わせや、帰宅時の連絡などにお使いください。

韓国語翻訳の改善

一部の韓国語の駅名表記を、鉄道事業者が使用している公式の表記に差し替えました。

ナビゲーションに関する API の追加

バージョン 2.1.0 で追加された開発者向け API に関してですが、MiniTokyo3D クラスにマップの操作を行う次のメソッドが追加されました。

メソッド 説明
getCenter マップの中心座標を取得
setCenter マップの中心座標を設定
getZoom マップのズームレベルを取得
setZoom マップのズームレベルを設定
getBearing マップの方角を取得
setBearing マップの方角を設定
getPitch マップの傾きを取得
setPitch マップの傾きを設定
easeTo マップをアニメーション付きで移動
flyTo マップを飛行をイメージしたアニメーション付きで移動
jumpTo マップをアニメーションなしで移動

詳細は Mini Tokyo 3D 開発者ガイドをご覧ください。

追跡中のズームレベル・傾きの変更

これまで列車や旅客機を追跡中は、画面上のボタンやキーボードを使ったズームレベルと傾きの変更はできませんでしたが、変更可能になりました。

マウスオーバー時の反応改善

列車や旅客機とマウスオーバーのポップアップが重なっていると、マウスを別の列車や旅客機上に動かしても表示が切り替わらない場合がありましたが、すぐに切り替わるように動作を改善しました。

フルスクリーンモードの不具合修正

フルスクリーンモードで駅名検索のテキストフィールドや追跡時の列車時刻表が表示されない不具合を修正しました。

Edge ブラウザにおける駅名検索の不具合修正

Edge ブラウザで、駅名の自動補完リストから駅を選択しても移動しない不具合を修正しました。

Mini Tokyo 3D 開発者向け機能の提供をスタート

f:id:nagixx:20200612154559j:plain

ほぼ 1 年前から開発を開始した、東京の公共交通のリアルタイム3Dマップ Mini Tokyo 3D のバージョン 2.1 をリリースしました。

おかげさまで Mini Tokyo 3D は第3回東京公共交通オープンデータチャレンジ最優秀賞を受賞したほか、2019年度 VLED 勝手表彰貢献賞GitHub スター数 1,300 超えなど、各方面から熱いご支持をいただいています。

Mini Tokyo 3D の内容や開発の経緯については、すでに記事がいくつか出ているほか、日々の開発状況のツイートをまとめた Togetter まとめをご覧いただくとして、本記事では新バージョンで導入したクリエーター・開発者向けの機能についてご紹介します。

Mini Tokyo 3D のマップを Web ページに埋め込む

Mini Tokyo 3D のマップを Web ページに埋め込むのはとても簡単。Mini Tokyo 3D は地図タイルに Mapbox のサービスを利用しているため、まず Mapbox のアクセストークンを入手しましょう。Mini Tokyo 3D 開発者ガイドの「Mapbox アクセストークンの入手」の手順に従って、アクセストークン文字列を取得します。

そして埋め込みを行いたい Web ページのヘッダに、スタイルシートJavaScript ファイルのリンクを追加。 

<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mini-tokyo-3d@latest/dist/mini-tokyo-3d.min.css" />
  <script src="https://cdn.jsdelivr.net/npm/mini-tokyo-3d@latest/dist/mini-tokyo-3d.min.js"></script>
</head>

ページ本文には、マップを表示する <div> エレメントを配置して、<script> エレメントに MiniTokyo3D オブジェクトを作成する JavaScript コードを記述します。オブジェクトに渡すオプションには、 <div> エレメントの ID と、先ほど入手した Mapbox アクセストークンを指定するのを忘れずに。

<body>
  <div id="mini-tokyo-3d" style="width: 400px; height: 400px;"></div>

  <script>
    const options = {
      container: 'mini-tokyo-3d',
      secrets: {
        mapbox: '<Mapbox アクセストークン>'
      }
    };
    const mt3d = new MiniTokyo3D(options);
  </script>
</body>

はい、これだけです。 <div> エレメントのスタイルを編集して好きなサイズにしたり、ページいっぱいに表示したりしてくださいね。

Mini Tokyo 3D API でカスタマイズ

Mini Tokyo 3D が提供する API を利用すれば、マップの表示内容や動作を柔軟にカスタマイズすることができます。上記の例のコードに、次のオプションを追加してみましょう。

  <script>
    const options = {
      container: 'mini-tokyo-3d',
      secrets: {
        mapbox: '<Mapbox アクセストークン>'
      },
      center: [139.767, 35.6806],
      zoom: 16
      bearing: 90,
      pitch: 15
    };
    const mt3d = new MiniTokyo3D(options);
  </script>

centerzoombearingpitchはそれぞれマップの初期座標(経度・緯度)、ズームレベル、方角(北を 0 として反時計回りの角度)、傾き(真上を 0 とした角度)を表しています。デフォルトだと東京駅周辺を南方向から見たマップになりますが、この設定では新宿駅周辺を東方向やや上方から眺めた感じになります。

利用可能なオプションの詳細は、開発者ガイドの「Mini Tokyo 3D API」を参照してください。

バージョン 2.1 は初めての開発者向け機能のリリースということで、まだオブジェクト作成時の初期設定オプションしか変えることができませんが、今後のリリースではマップ内オブジェクトの操作やイベントコールバック、プラグイン API を介したカスタムレイヤーの追加など、よりインタラクティブなコントロールを可能にすることを構想しています。

モジュールとしてアプリに組み込む

今回のリリースのタイミングで npm パッケージとしての公開を始めています。皆さんのアプリの部品としてお使いいただけます。

まず、Mini Tokyo 3D の npm モジュールをインストールし、皆さんのアプリケーションの package.json に登録します。

npm install mini-tokyo-3d --save

CommomJS 形式でモジュールを読み込む場合は、コードの先頭で次のように記載します。

const MiniTokyo3D = require('mini-tokyo-3d');

ES6 形式でモジュールを読み込む場合は、コードの先頭で次のように記載します。

import MiniTokyo3D from 'mini-tokyo-3d';

アプリケーションのコード内で、次のようにして MiniTokyo3D オブジェクトを初期化します。options オブジェクトの container には Mini Tokyo 3D がマップを表示する HTML エレメントの ID を指定します。また、secrets.mapbox には、上のステップで入手した Mapbox アクセストークンを指定します。

const options = {
  container: '<コンテナエレメントの ID>',
  secrets: {
    mapbox: '<mapbox アクセストークン>'
  }
};
const mt3d = new MiniTokyo3D(options);

こちらも詳細は、開発者ガイドの「モジュールとしてアプリに組み込む」を参照してください。

スポンサー募集中!

最後になりますが、GitHub スポンサーの募集も始めました。皆さんのご支援は今後の開発の大きなモチベーションになりますので、Mini Tokyo 3D を気に入っていただけましたらぜひよろしくお願いします!

github.com

Chart.js のチャートを格段に綺麗に見せるカラーパレットプラグイン

f:id:nagixx:20181126195706p:plain

動的なチャートを作って Web ページに貼り付けるのに Chart.js はとても便利ですが、難点は1つずつ色の指定をしなければいけないところです。例えばこんな感じで棒グラフを作るとして、Dataset ごとに背景色を指定する必要があります(色を指定しないとグレー一色になってしまう)。

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.js"></script>
<canvas id="myChart"></canvas>
<script type="text/javascript">
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['2015年', '2016年', '2017年', '2018年'],
    datasets: [{
      label: 'Dataset 1',
      data: [4, 15, 12, 23],
      backgroundColor: 'rgb(255, 0, 0)'
    }, {
      label: 'Dataset 2',
      data: [10, 9, 3, 16],
      backgroundColor: 'rgb(0, 255, 0)'
    }, {
      label: 'Dataset 3',
      data: [12, 20, 14, 3],
      backgroundColor: 'rgb(0, 0, 255)'
    }]
  }
});
</script>

ところがここで面倒臭がって赤、緑、青でいいや、などとしてしまうと、このような絶望的な見栄えのチャートになってしまいます。

chart1

気の利いたカラーパレットをデフォルトで適用してくれれば良さそうなものの、実際のところそうはなっておらず、ページ作成者のデザインセンスがモロに出てしまうのは深刻な問題です。

そういうわけで、ここはひとつプラグインでも作るか、ということでできたのが chartjs-plugin-colorschemes です。使い方は下記の順番で .js ファイルを <head>〜</head> で読み込んでおくだけ。先ほどの backgroundColor のプロパティは削除しておきます。

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<script type="text/javascript" src="https://github.com/nagix/chartjs-plugin-colorschemes/releases/download/v0.2.0/chartjs-plugin-colorschemes.min.js"></script>

chart2

なんということでしょう。あんなにみすぼらしかったチャートが(以下略)。

現時点では、カラーパレットはデータビジュアライゼーション界隈でポピュラーな ColorBrewerTableau から拝借しております。ColorBrewer はペンシルベニア州立大学の地理学者 Cynthia A. Brewer が考案したカラーパレットで、人間の視覚を考慮して色の区分が明瞭になるように工夫されており、連続的 (Sequencial)、発散的(diversing)、定性的(Qualitative)の3つのカテゴリそれぞれに複数の色のセットが用意されています。Tableau はシアトルに本社を置くタブローソフトウェアが開発しているデータビジュアライゼーションの商用製品で、こちらもチャートでの利用に最適なカラーパレットを数多く備えています。

デフォルトでは次の brewer.Paired12 カラーパレットが自動的に適用されますが、自分で指定するには次のように 一覧から選んだカラースキーマをオプションに記述します。

brewer.Paired12:                        
var myChart = new Chart(ctx, {
  // ...
  options: {
    plugins: {
      colorschemes: {
        scheme: 'brewer.Paired12'
      }
    }
  }
});

下記にいくつかのサンプルを載せておきます。インタラクティブに動作するチュートリアルページでも色を確認しながら、ぜひ色々なカラーパレットをお試しください。

chart3

chart4

chart5

chart6

拡大縮小・スクロールを無効にする方法がiOS 11.3 Safariで効かなくなった問題と解決法

その昔、スマホ向けWebページの拡大縮小・スクロールをさせたくない場合にはviewportuser-scalable=noを指定するという手軽な方法が利用可能でしたが、2016年のiOS 10のリリース以降はこの方法が使えなくなったため、JavaScriptを使ってtouchstartイベントやtouchmoveイベントのハンドラでpreventDefault()を呼ぶ方法がポピュラーになっていると思います。

が、つい最近iOS 11.3 Safariでこの方法が効かなくなっていることに気づきました。この変更は結構多くのライブラリの動作に影響を与えており、一部の開発者の間で騒ぎになっていてWebKitのバグにも登録されました。しかし、WebKitの開発者ではこれはバグではなく想定された仕様変更だということで、ページ製作者の側が対応する必要があります。

Bug 182521 – REGRESSION (iOS 11.3 beta): touchmove preventDefault() no longer respected

具体的にどんな変更があったかというと、bodydocumentwindowtouchstartおよびtouchmoveイベントハンドラのデフォルト動作が、passiveになったとのこと。passiveイベントハンドラではpreventDefault()の呼び出しが無視されてしまうわけです。イベントハンドラの動作オプションについては下記の記事を参考にしてください。

そもそもなんのための変更かというと、これによりイベントハンドラ内で拡大縮小・スクロール動作が抑止されないことがあらかじめわかるため、スクロール動作のレスポンスがかなり改善されるのです。

では、どうすれば以前のような動作にできるかというと、MDNにヒントがあります。一番下の方の「passive なリスナーを用いたスクロールのパフォーマンスの改善」というところです。よく見るとChromeも55からtouchstartおよびtouchmoveのデフォルト動作がpassiveになってますね。

ちょっとコードを抜粋して手を加えると、このような感じです。現在、ほぼすべてのブラウザがaddEventListener()の第3引数にpassiveプロパティを含むオブジェクトを受け付けるようにはなっているのですが、互換性を重視するなら下記のような検出コードを使うとよいと思います。

/* "passive" が使えるかどうかを検出 */
var passiveSupported = false;
try {
    document.addEventListener("test", null, Object.defineProperty({}, "passive", {
        get: function() {
            passiveSupported = true;
        }
    }));
} catch(err) {}

/* リスナーを登録 */
document.addEventListener('touchstart', function listener(e) {
    /* do something */
e.preventDefault(); }, passiveSupported ? { passive: false } : false);
document.addEventListener('touchmove', function listener(e) { /* do something */
e.preventDefault(); }, passiveSupported ? { passive: false } : false);

最後におまけ。こちらのページにも上記の修正を入れておきました。

Drawing a real-time Bitcoin chart using Chart.js

(*日本語の記事はこちら)

Chart.js is a popular JavaScript chart library that enables to create dynamic, beautiful charts easily. I recently made chartjs-plugin-streaming, a Chart.js plugin for live streaming data with the auto-scroll feature. It is suitable for IoT-related use cases such as sensor data monitoring, and when I was looking for some real examples of streaming data, I noticed that it is also good for displaying real-time digital currency trading data that the digital currency exchanges provide. So, I just tried that out.

It is becoming more common for the exchanges to deliver trading data efficiently using WebSocket. Some of them require authentication, but in this article, let's create trading charts for the exchanges that provide public API without authentication.

First example: Bitfinex WebSocket API

First, we need to include the following required libraries.

Put the following tags between <head> and </head>.

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.js"></script>
<script type="text/javascript" src="https://github.com/nagix/chartjs-plugin-streaming/releases/download/v1.1.0/chartjs-plugin-streaming.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.js"></script>

Next, let's add a canvas to the page. Include an id so that you can identify the chart later.

<canvas id="Bitfinex"></canvas>

Then, prepare an object for buffering data, which is buf. Set the array that has two empty array elements to the property with the same name as canvas's id. The first array is for 'buy' prices and the second is for 'sell' prices.

<script type="text/javascript">
var buf = {};
buf['Bitfinex'] = [[], []];
</script>

Now, let's access the real data. We are going to collect real-time trading data using WebSocket from Bitfinex in Hong Kong, which is one of the largest exchanges by BTC/USD trading volume. You can subscribe trading data of the Bitcoin/US dollar pair by sending a request like below to the URI wss://api.bitfinex.com/ws provided by Bitfinex. (See Bitfinex WebSocket API for details)

{
    "event": "subscribe", // subscribe request
    "channel": "trades",  // trade data
    "pair": "BTCUSD"      // Bitcoin/US dollar
}

The data you will receive through the callback function is like this.

[
    5,             // 0: channel ID
    'te',          // 1: message type
    '1234-BTCUSD', // 2: sequence ID
    1443659698,    // 3: timestamp
    236.42,        // 4: price
    0.49064538     // 5: amount bought (positive) or sold (negative)
]

As we only need the trade type, the timestamp (X-axis) and the price (Y-axis), the filtered data is stored into the buffer on every message receipt using the following code.

var ws = new WebSocket('wss://api.bitfinex.com/ws/');
ws.onopen = function() {
    ws.send(JSON.stringify({      // send subscribe request
        "event": "subscribe",
        "channel": "trades",
        "pair": "BTCUSD"
    }));
};
ws.onmessage = function(msg) {     // callback on message receipt
    var response = JSON.parse(msg.data);
    if (response[1] === 'te') {    // Only 'te' message type is needed
        buf['Bitfinex'][response[5] > 0 ? 0 : 1].push({
            x: response[3] * 1000, // timestamp in milliseconds
            y: response[4]         // price in US dollar
        });
    }
}

Finally, we need to configure the chart. Regarding the details of the chart customization, please refer to the Chart.js official documentation. The key points here are that the 'realtime' scale that the chartjs-plugin-streaming plugin provides are set to the X axis, and that the entire data stored in the buffer is added to the chart in the onRefresh callback function that is called at a regular interval (every second by default).

var id = 'Bitfinex';
var ctx = document.getElementById(id).getContext('2d');
var chart = new Chart(ctx, {
    type: 'line',
    data: {
        datasets: [{
            data: [],
            label: 'Buy',                     // 'buy' price data
            borderColor: 'rgb(255, 99, 132)', // line color
            backgroundColor: 'rgba(255, 99, 132, 0.5)', // fill color
            fill: false,                      // no fill
            lineTension: 0                    // straight line
        }, {
            data: [],
            label: 'Sell',                    // 'sell' price data
            borderColor: 'rgb(54, 162, 235)', // line color
            backgroundColor: 'rgba(54, 162, 235, 0.5)', // fill color
            fill: false,                      // no fill
            lineTension: 0                    // straight line
        }]
    },
    options: {
        title: {
            text: 'BTC/USD (' + id + ')', // chart title
            display: true
        },
        scales: {
            xAxes: [{
                type: 'realtime' // auto-scroll on X axis
            }]
        },
        plugins: {
            streaming: {
                duration: 300000, // display data for the latest 300000ms (5 mins)
                onRefresh: function(chart) { // callback on chart update interval
                    Array.prototype.push.apply(
                        chart.data.datasets[0].data, buf[id][0]
                    );            // add 'buy' price data to chart
                    Array.prototype.push.apply(
                        chart.data.datasets[1].data, buf[id][1]
                    );            // add 'sell' price data to chart
                    buf[id] = [[], []]; // clear buffer
                }
            }
        }
    }
});

Below is the completed chart. You can see that the chart is scrolling from the right to the left slowly.

Bitfinex

Bitstamp WebSocket API

The next example is Bitstamp, an exchange in UK, which is the central trading hub in Europe. Bitstamp uses Pusher, a Pub/Sub messaging library for real time WebSocket streaming. Therefore, the data subscription code is much simpler. (See Bitstamp WebSocket API for details)

The data you will receive through the callback function is like this.

{
    id: 17044523,            // trade unique ID
    amount: 1,               // amount
    price: 2496.21,          // price
    type: 1,                 // trade type (0: buy, 1: sell)
    timestamp: "1499472674", // timestamp
    buy_order_id: 47485421,  //	buy order ID
    sell_order_id: 47485426  //	sell order ID
}

Below is the code that receives data. It also takes only the trade type, the timestamp and the price.

buf['Bitstamp'] = [[], []]; // prepare buffer
var pusher = new Pusher('de504dc5763aeef9ff52'); // Pusher key for Bitstamp
var channel = pusher.subscribe('live_trades'); // subscribe live trade data
channel.bind('trade', function (data) { // callback on message receipt
    buf['Bitstamp'][data.type].push({
        x: data.timestamp * 1000, // timestamp in milliseconds
        y: data.price             // price in US dollar
    });
});

The configuration of the chart is the same as above except for the id. Below is the completed chart.

Bitstamp

BTC-E WebSocket API

BTC-E is also a popular exchage located in Bulgaria. It uses Pusher for streaming as well. (See BTC-E WebSocket API for details)

Below is the data you will receive.

[
    [
        "buy",       // 0: trade type
        "2476.999",  // 1: price
        "0.08863539" // 2: amount
    ]
]

As timestamps are not included in case of BTC-E, set the current time using Date.now().

buf['BTC-E'] = [[], []]; // prepare buffer
var pusher = new Pusher('c354d4d129ee0faa5c92'); // Pusher key for BTC-E
var channel = pusher.subscribe('btc_usd.trades'); // subscribe BTC/USD trades
channel.bind('trades', function (dataset) { // callback on message receipt
    dataset.forEach(function(data) {
        buf['BTC-E'][data[0] === 'buy' ? 0 : 1].push({
            x: Date.now(), // timestamp in milliseconds
            y: data[1]     // price in US dollar
        });
    });
});

BTC-E

BitMEX WebSocket API

BitMEX is an exchange in Hong Kong, which is famous for high leverage trading. It provides a normal WebSocket API. Below are the request and the message to be received. (See BitMEX WebSocket API for details)

{
    "op": "subscribe", // subscribe request
    "args": [
        "trade:XBTUSD" // Bitcoin/US dollar
    ]
}
{
    table: "trade",
    action: "insert",
    data: [
        {
            timestamp: "2017-07-09T01:39:30.866Z", // timestamp
            symbol: "XBTUSD",         // currency pair symbol
            side: "Buy",              // trade type
            size: 34,                 // amount
            price: 2548.9,            // price
            tickDirection: "ZeroPlusTick", // tick direction
            trdMatchID: "34d6de97-5d54-3431-e505-ffc3bc8c58ef",
            grossValue: 2039284,      // gross value 
            homeNotional: 0.02039284, // notional in Bitcoin
            foreignNotional: 52       // notional in US dollar
        }
    ]
}

The code and chart are as follows.

buf['BitMEX'] = [[], []]; // prepare buffer
var ws = new WebSocket('wss://www.bitmex.com/realtime');
ws.onopen = function() {
    ws.send(JSON.stringify(    // send subscribe request
       "op": "subscribe",
       "args": [
            "trade:XBTUSD"
        ]
    }));
};
ws.onmessage = function(msg) { // callback on message receipt
    var response = JSON.parse(msg.data);
    response.data.forEach(function(data) {
        buf['BitMEX'][data.side === 'Buy' ? 0 : 1].push({
            x: data.timestamp, // timestamp
            y: data.price      // price in US dollar
        });
    });
}

BitMEX

CoinCheck WebSocket API

The last one is Japanese exchange, CoinCheck. Only BTC/Japanese-yen rates are delivered here, so let's display them. Below are the request and the message to be received. (See CoinCheck WebSocket API for details)

{
    "type": "subscribe",        // subscribe request
    "channel": "btc_jpy-trades" // Bitcoin/Japanese yen
}
[
    9856377,    // trade ID
    "btc_jpy",  // currency pair
    "289544.0", // price
    "0.0367",   // amount
    "sell"      // trade type
]

The code and chart are as follows.

buf['CoinCheck'] = [[], []];
var ws = new WebSocket('wss://ws-api.coincheck.com/');
ws.onopen = function() {
    ws.send(JSON.stringify({        // send subscribe request
        "type": "subscribe",
        "channel": "btc_jpy-trades"
    }));
};
ws.onmessage = function(msg) { // callback on message receipt
    var response = JSON.parse(msg.data);
    buf['CoinCheck'][response[4] === 'buy' ? 0 : 1].push({
        x: Date.now(), // timestamp in milliseconds
        y: response[2] // price in Japanese yen
    });
}

CoinCheck WebSocket API

There are many other digital currency exchanges that provide API to deliver real-time trading data. The API and data format are slightly different each other, but you can draw a chart with minimum modification. I hope this article helps you try out for yourself.