カテゴリー別アーカイブ: JavaScript

SoundCloudはどうやってタブ間で同期を取っているか

僕の好きな音楽共有サイトの一つに「SoundCloud」というサービスがあります。
日本人もたくさん投稿していて、カッコイイEDMもかなり投稿されてるので、作業用BGMによく利用させてもらっています。
そのSoundCloudでどうやって実装されてるのか興味深い箇所があったので、調べる過程も含めて共有します。

複数タブ(又はウィンドウ)で情報を同期してくれる機能が便利で不思議

20130312
SoundCloudを複数ページ開いた状態で、片方のタブで再生中に別タブの再生ボタン(上の画像で黒で囲んである左側のボタン)を押すと、もう片方の再生が止まるんですよね。
また、ボリュームコントロール(上の画像で黒で囲んである右側のボタン)も、片方のタブで調整するともう片方のタブに自動的に反映されます。
YouTubeでタブ毎に音量調整する癖がついてたので、「おおっ」と少し感動してしまいましたw

どうやって実装してる?

JavaScriptで他のタブに直接アクセスする事は出来ないはず。
じゃあサーバを介して情報をやりとりしてるのかな?と思ってChrome devtoolsで通信を監視してみました。

20130312_2
通信してない…!
ボリュームコントロールをいじってみた所、通信が発生していないのに別タブで再生中の楽曲の音量が変わりました。
つまり、クライアント側の処理だけでタブ間同期を実現しているということ。

クライアントでタブ間で共通して使えるリソースといえば…

CookieかlocalStorageを使っている可能性が高い。
→どちらかを使っていれば、ボリュームを変更した時にそのどちらかの値が書き換わっているはず!

ということで、またdevtoolsで以下のjavascriptを実行してみました。

cookieの値が変化しているか確認

  1. ボリュームを変更する操作をする前に以下を実行。
    console.log(document.cookie);
  2. 実行結果をコピーして、SoundCloudの画面上のボリュームを変更。
  3. 以下を実行。
    console.log(document.cookie === '__qca=...'); // 右項は上記1.の実行結果の文字列のコピペ
  4. trueと表示されたらCookieの値が変更されていない。falseなら変更されてる。

実行結果

true
ふむ。Cookieはこの機能に利用されていない様子。

localStorageの値が変化しているか確認

  1. ボリュームを変更する操作をする前に以下を実行。(localStorageはオブジェクトなので、テキスト化するためにちょっと余計な事してます)
    console.log(escape(JSON.stringify(localStorage)));
  2. 実行結果をコピーして、SoundCloudの画面上のボリュームを変更。
  3. 以下を実行。
    console.log(escape(JSON.stringify(localStorage)) === '%7B%22V2%3A%3Alocal...'); // 右項は上記1.の実行結果の文字列のコピペ
  4. trueと表示されたらlocalStorageの値が変更されていない。falseなら変更されてる。

実行結果

false
きた!!!変更されてるのでタブ間同期にlocalStorageが利用されている可能性が高い。
localStorageの内容を見ると「V2::local::volume-settings: “{“volume”:0.5}”」というフィールドがあったので、この値を各タブで共有して音量情報を同期してるっぽいです。

どうやってlocalStorageの変更を監視しているか?

localStorageの変更時に’storage’イベントが発火するようなので、多分それを利用してるんじゃないかと(ソースは読んでない)。
このイベント、localStorageを変更したウィンドウ以外のウィンドウでも発火するんですね。意外。

W3Cの仕様書の「4.4 The storage event」の章に以下の記述があるので、これがタブ間の同期に利用されている仕様なんでしょうかね。。(英語得意じゃないのであんまり自信ないです)

When this happens, the user agent must queue a task to fire an event with the name storage, which does not bubble and is not cancelable, and which uses the StorageEvent interface, at each Window object whose Document object has a Storage object that is affected.

まとめ

  • SoundCloudの複数ウィンドウ間の同期にはlocalStorageのstorageイベントが使われてるっぽい。
  • 異なるウィンドウ同士の同期にlocalStorageが使える、ということだけでも覚えておくと役に立つかも。

IntelliJでTypeScriptをサクサクコンパイル

TypeScriptもやっぱりIntelliJだよね – しおしおの雑記帳
↑の記事を拝見して、IntelliJ IDEAのメニュー項目からTypeScriptをコンパイルできることを知りました。

そしてそのメニュー項目に[Command + Enter]のショートカットを割り当ててみたらかなり便利だったので、その手順をシェアしておきます。
(ショートカットキーがCommand + Enterなのは私がFlash IDEを使っていた名残なのでw、別のショートカットにして頂いても構いません)

まずはTypeScriptをコンパイルするメニュー項目を追加

Settings -> External Tools から、以下のようにコンパイル用のメニュー項目を設定します。
(ここは上記の記事と同じ内容ですが、一応書いておきます。。)
130217_1

追加したメニュー項目にショートカットキーを割り当てる

Settings -> Keymap を開き、一覧から先ほど追加したメニュー項目を探します(右上のインクリメンタルサーチが便利です)。
見つかったらその項目をダブルクリック -> Add Keyboard Shortcut を選択して、ショートカットキーを設定します。
130217_2

多分、デフォルトでは別の機能に[Command + Enter]のショートカットが割り当たってしまっていると思うので、不都合ある方は別のショートカットキーを設定してください。
私は気にせず上書きしちゃいましたw

完了!

これで設定は完了です。
TypeScriptファイル(*.tsファイル)を編集中にCommand + Enterキーを押すと、サクッとjsファイルが生成されるようになっているはず。
確認してませんが、WebStormでも同じことが出来そうですね。

このショートカットキーの割り当て設定は、他の用途でも効率アップに結構役立つので是非試してみてくださいー。

第二回全国統一HTML5実技コンテストで落選しました

jsdo.itさん主催の第二回全国統一HTML5実技コンテストにて、以下の作品が落選しました。
とても悔しい…!

これ、制作に1週間くらいかけてて結構気合が入ってたんだけど、ゲームっぽい派手さを目指したせいか、ウェブ的にあまり馴染みのない画面に仕上がっちゃったんだよなー…。
多分、そのあたりのユーザビリティの悪さが落選に繋がったんだと思います。

次回の「第三回全国統一HTML5実技コンテスト」のテーマは『HTML5で表情豊かに表現する「顔文字(・∀・)」』。
回を重ねるごとにテーマの難易度が上がっている気がしますが、今回の反省点を踏まえて早いとこアイディアを固めなくては!