2011-07-27
iPhoneをhtmlなプレゼンのリモコンにするための何か
会社でid:gfxさんがWebSocketを使って夢が広がるエクストリームなプレゼンをしてくれたので、海外の空気を読まずに作ってみました。
https://github.com/zentooo/SkyWalk
端的に言うと、Socket.IOを通じてリモコンとなるiPhone/Android(?) のブラウザでキャッチしたスワイプイベント(swipe eventというものはないので、実はtouch系でエミュレートしてるだけ)の情報をhtmlプレゼンの側に送っています。
プレゼン側jsでは/socket.io/socket.io.js/とcontroller.jsを読み込んだ上でswipeLeft, swipeRightなどのイベントをキャッチしてプレゼンの操作と結びつけます。
例えばこんなかんじ
document.addEventListener("swipeRight", prev, false); // prevは前のスライドに戻る関数 document.addEventListener("swipeLeft", next, false); // nextは次のスライドに進む関数 document.addEventListener("tap", showNext, false); // showNextは同スライド内の次の行を表示する関数
なんで右swipeを戻りで左swipeを進むにしているかは、普段iPhone/Androidを操作している時の指の動かす方向を考えればわかるとおもいます。
Socket.IOを使っているので、Socket.IO protocolを喋るサーバをローカルで動かして、同じ無線LANに接続したiPhoneから操作する、という使い方になります。
それじゃhtmlプレゼンなのにどこかに適当にupしてもうごかねーじゃねーか、っていう話もありますが、どこかにupしたものをみる場合は普通にクリックで次に進めるなりjkで移動したりするなりすればいいのです。
サーバ側はライブラリ化してなくとサーバアプリと密結合してますが、その辺はあんまり切り離す意味もないので。今はnodeな実装しか用意してないですが、そのうちPerlのPocketIOとかその他の言語分も用意するかもしれません。
てか、デモとかないとよくわかんないですねこれは。
あと、ひさしぶりにSocket.IO触ったら色々変わっててびびった。
2011-03-29
Node.jsひみつの花園、readline module
Nodeの公式docにはだいたいの標準モジュールのドキュメントがそろっているのだけど、なぜかreadlineという名前のモジュールだけはundocumented。-> http://nodejs.org/docs/v0.4.4/api/readline.html
中身としては、readlineのない環境(?)で簡易なGNU Readlineの機能を提供するのと、input streamから一行読み込んで何かするという枠組みの提供、っぽい。
Nodeでちょっとした機能付きの(んで標準replとは別の)replを作りたいときってどうするんかいな?と前から思ってて、Replicaを作る段になって「これ使えそう」と手探りで調べた内容をメモ。ソース見ただけともいう。
var readline = require("readline"); // First argument (input) should be Readable Stream, second (output) should be Writable Stream // here, input -> STDIN, output -> STDOUT var repl = readline.createInterface(process.stdin, process.stdout); // set prompt string repl.setPrompt("X / _ / X "); // print prompt string to output stream repl.prompt(); // get input stream var istream = repl.input; // get output stream var ostream = repl.output; // called when read one-line-input from input stream repl.on("line", function(line) { }); // called when closed repl.on("close", function() { });
他にもインタフェースあったような気がするけど、とりあえずこれだけで十分。ちょっと面白いのは、createInterfaceに指定するinput, outputが別にstdin, stdoutでなくてもいい件。まあ、inputがstdinじゃなかったらそもそもReadline機能の意味があまりないわけだが…
コードも短いしドキュメントないからdeprecatedなモジュールなんじゃね?と思う人もいるかもしれないけど、標準のrepl moduleが確かこれに依存してたのでそんなに簡単には消えない…はず。ryanに直接聞いたとかじゃないから、確証はないけど。
2011-03-21
Replica - terminalのreplからブラウザ上のJavaScriptを実行する
マクラ
このまえ会社でちょっと特殊なJavaScript replを作ったのだけど、もうちょっと汎用的なものを作ってみたかったのでNodeで書いたっていうお話。Nodeで書くこと自体にあまり深い意味はないんですが、Socket.IO使えばcross-browser/cross-originでのpushが簡単だから、ということで。
Install
Replica - http://github.com/zentooo/Replica
npm install replicaでインストールできます。ちなみにNodeについてはv0.5.0-pre、ブラウザについてはFirefox4.0 RC2, Chrome(10.0.648.151) / Chrome-dev(11.0.696.14) しかテストしてないです。多分、その他の環境でも動くんじゃないでしょうか。
npmにゃんでのインストールがずっこける場合は、npm再インスコするかIssac先生にレポートを投げて
npm install socket.io npm install cli npm install node-static git clone git://github.com/zentooo/Replica.git cd Replica/bin ./replica
のように、直で叩くと起動できると思います。
Inside
内容としては、コマンドラインからreplを立ち上げて、localで立ち上がるWebサーバのとあるページにアクセスして、replにJavaScriptのコードを打ち込むとブラウザ側でそいつが実行される、というものです。
ここまで書いたところで分かる人は一発で分かると思いますが
replで行を受け取る -> ブラウザにpush -> 受け取ったものをeval
しているだけですね。本当にありがとうございました。
Behavior
起動しているところはこんな感じです。
基本はこのページのコンテキストでJavaScriptを実行出来るだけなのですが、青文字のでかい"Replica"はブックマークレットへのリンクになっていて、このブックマークレットを利用すると任意のサイトでReplica replからJavaScriptを投げられるようになります。ひょっとしたら役に立つ場合もあるかもしれませんね。
ページ左下のフォームからJavaScriptコードのブロードキャストもできるので、試す場合は複数マシン/複数ブラウザでやると面白いです。
ネタバレ
分かる人は分かるかと思いますが、これはJames Coglan先生のTerminusの単なるNode版ですね。jcoglan先生のTerminusは自作JSライブラリを駆使した労作なのですが、僕のは色々な外部ライブラリにのっかった手抜き版です。しいて勝っているところをあげるとしたら、起動/終了の速さぐらいでしょうか。
jcoglan先生は面白いものを一杯作っているので、興味がある人はgithubを覗いてみましょう。
2010-11-14
Shindigのwave featureで実現する簡単なお絵かきチャット feat. node.js and Socket.IO
JavaScript, OpenSocial, node.js
タイトル書いてから気づいたけど、別にチャット機能とかなかった。
http://d.hatena.ne.jp/zentoo/20100821/1282407303
以前に書いた↑のエントリでは、Shindigのextrasに入ってるwave featureを調べてみて、「あーこれShared Stateはコンテナ側で何とかしてくれって形ですね」という所で終わっていた。今回はいささか無理くりながらShared Stateの実装を行ってみた。
Shared Stateに用いるサーバはnode.jsで実装する。構成としては、Shindigが8080番で待ち受け、node with Socket.IOを9000番で起動。Socket.IOはnode.jsで簡便にWebSocketのサーバサイドを実現するライブラリとして知られているが、WebSocketが動かない環境ではxhr-multipartやxhr-pollingにfallbackするようになっている。そういったサーバ側ライブラリの例に漏れず、クライアント側に必要なライブラリをサーブするための口も用意してあり、Socket.IOでは
http://{host}/socket.io/socket.io.js
がそれに相当する。クライアントライブラリは上記に上げた以外にも様々なプロトコルに対応しており、node.jsとは独立に使うこともできるようだ。
http://github.com/LearnBoost/Socket.IO
Shindigのwave featureではstateの更新が起こった場合にgadgets.rpcを使ってまずcontainer側に処理を移し、そこから何らかの形でShared Stateを実現するための受け口にアクセスする、という形式になっている。なので、多少まどろっこしいが今回のwave gadgetにおける通信の流れを書くと以下のようになる。
wave gadgetでstateを更新 → gadgets.rpcで更新データがcontainer側へ → container side JSから、Shared Stateサーバ(仮称)へデータを投げる → Shared Stateサーバがデータを受け取り、その他のクライアントへブロードキャスト → container側JSが受け取る → gadgets.rpcでwave gadgetへ → wave.setStateCallback()で指定したコールバック関数が呼ばれる
あーまどろっこしい。とにかくこうなる。「Shared Stateサーバがクライアントから送信されたデータを受け取り、他のクライアントにブロードキャスト(もちろんpush)する」という部分がサーバサイドのキモになるわけだが、今回はSocket.IOがその辺をよろしくやってくれるので、該当部分のコードは以下のようになる。
socket.on('connection', function(client) { client.on("message", function(data) { client.broadcast(data); }); });
短っ!という感じだが、Socket.IOにはbroadcast()という名前で現在サーバに接続している他のクライアントにメッセージをブロードキャストするそのものズバリなメソッドがあるので、ほとんどやることはない。
もちろんこんな風にすると全てのwave APIを使っているガジェットの通信データが全部混じってしまうので、現実にはこんな実装をすることはないだろう。認証や永続化の仕組みも必要になる。でも今回は「とにかくShindigのwave featureを動かす」ことが目的なのでその辺は割愛。
これによってSocket.IO経由でクライアント間のメッセージングを行うことが可能になったので、後はcontainer側のhtmlでクライアント側ライブラリを読み込んでお膳立てをしてやるだけ。
<script type="text/javascript" src="http://localhost:9000/socket.io/socket.io.js"></script> <script type="text/javascript"> var socket = new io.Socket("localhost", { port: 9000 }); function initRpcs() { gadgets.rpc.register("wave_gadget_state", function(data) { socket.send( { type: "gadget_state", content: data } ); }); gadgets.rpc.register("wave_private_gadget_state", function(data) { socket.send( { type: "private_gadget_state", content: data } ); }); } function initSocket(version) { var frameId = "remote_iframe_0"; socket.on('message', function(data) { gadgets.rpc.call(frameId, "wave_" + data.type, null, data.content); }); socket.connect(); } gadgets.rpc.register('wave_enable', initSocket); initRpcs(); </script>
ちなみに上記コードのframeIdとかは、Shindig付属のsamplecontainerで動かす前提で決め打ちです。
で、wave APIのうちstate関係のAPIが動くようになったんだけどもこれらを使って何しよう、ということになったので、とりあえずcanvasを使ったお絵かき共有アプリ(めっちゃ単純なやつ)を作ってみた。以下が、ローカルでそのお絵かきアプリ on wave APIを動かしている動画。
元の画面がデカいのでちょっと解りづらいが、左でFirefox、右でChromeを立ち上げている。一方のブラウザで書いた線の座標データがwave.getState().submitValue()によってgadgets.rpc経由でnodeへ送られ、nodeがclient.broadcast()によって他方のブラウザへ送信することでcanvasの同期を実現している。ちょっと変だが、自分の描いた線は赤色で、他のクライアントから描画した線は青色になるという仕様。
この例では、「描画した(というかfillRectした)点の座標をバッファに突っ込んでいき、バッファが一定まで埋まったらそのデータをsubmitDeltaする → 受け取った側は配列をなめて描画」というナイーブなアルゴリズムで同期を行っている。canvasの同期に定石アルゴリズム?があるかどうかは分からないが、現実にはもっとマシな方法を使うだろう。できるだけサーバ側との通信を抑えつつ、スムーズな同期を行う方法…うーんマンダム。WebSocketも、どのブラウザでも使えるようになるのは先だろうし。
とにもかくにも、wave featureを動かすことには成功したのでめでたしめでたし。zentooo先生のShindigの上で動くwave gadgetが見れるのは愛と勇気と缶ビールだけ!
一応ではあるが、ソースもあげといた。
2010-11-10
v8botをTwitter用にportした
某所の某チャンネルで、v8botなるものを見つけたので、Twitterでも動くようにしてみた。動くようにしてみたというか、サンドボックス周り以外はイチから書いているし、機能は一つしか移植していない。元のヤツは参考程度。
ReplyでJavaScriptの式を渡すと、内蔵V8 Engineで解釈した結果をReplyしてくれる。user streamからとってるので結構高速に返してくれる(はず)。
例えばこのように。
@v8bot [1, 2, 3].forEach(function(i) { console.log(i); });
単にJavaScriptのコードを投げて遊んでみるもよし。
俺こそはスーパーハカーだと言う人は、ローカルのファイルシステムにアクセスを試みるコードを投げて僕の家のサーバに攻撃を試みるもよし。Twitterだからそういうことしてもすぐばれちゃうけどね。




