ブラウザ上でRubyでProcessingする
思い立ってProcessing Advent Calendar 2015に参加させていただきました。12/20担当、kitaoです。よろしくお願いします。
かつて、「Rubyを使って気軽にクリエイティブコーディングしたい!」という人がたくさんいるに違いないと確信し、Processing.rbを作ったりしたのですが、心残りなこととして、JavaのProcessingライブラリを利用していたため、ブラウザ上で簡単に公開できないという点がありました。
あれから時代は流れ、今はp5.jsやOpalといったブラウザと相性の良いライブラリが大分実用的になって来ましたので、今回はブラウザ上でRubyで作成したProcessingのスケッチを動かすことにチャレンジしてみたいと思います。
Rubyで作成したスケッチをブラウザ上で動作させる
先に結論。
- Rubyで作ったスケッチを無事ブラウザで動かすことができました
- でも、手を抜くためにダーティハックを多用しています
- まだ実用には耐えられないので、真似しないほうが良いです
- そのうちきちんとしたライブラリにまとめるので、今回はお許しください
実際の手順を説明します。
使用するライブラリ
今回は、RubyをJavaScriptに変換できるOpalと、JavaScriptでProcessingのスケッチが作成できるp5.jsを利用します。
この2つのライブラリで、Rubyコード⇨JavaScriptコード⇨Processingスケッチと変換していく目論見です。
ちょっと脱線ですが、以前のp5.jsは3D機能が使えなかったのでProcessing.jsの方がいいかなと思っていたのですが、3Dに対応してぐっと使う気が高まりました。IDEさえ整備されれば今後はこちらが主流になりそうな気がします。
Opalとp5.jsの読み込み
まずはhtmlでOpalとp5.jsを読み込みます。今回は事前にライブラリをダウンロードするのではなく、CDNを使ってネットから直接取得します。
htmlに以下のscriptタグを追加します。
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.20/p5.js"></script> <script src="http://cdn.opalrb.org/opal/current/opal.min.js"></script> <script src="http://cdn.opalrb.org/opal/current/opal-parser.min.js"></script> <script>Opal.load('opal-parser');</script> <script type="text/ruby" src="./main.rb"></script>
p5.jsは普通に読み込んでいますが、Opalは、RubyからJavaScriptへの変換をブラウザ上で行わせるために、opal.min.js
に加えてopal-parser.min.js
も読み込んでいます。また、text/ruby
タイプのスクリプトを自動変換させるために、次の行のOpal.load('opal-parser');
でopal-parser
を起動させています。
最後の行でスケッチ本体であるmain.rb
を読み込んでいます。タイプ指定がtext/ruby
になっているところがポイントです。
Opalとp5.jsを接続する
これでRubyコードがブラウザ上で動作するようになったので、早速スケッチを作成したいところなのですが、残念ながらRubyからp5.jsの命令を呼ぶためには、接続用のコードを書かなければなりません。
OpalとJavaScriptの接続方法は、公式マニュアルのCompiled Ruby Codeの章に記載されています。ざっくり要約すると、
- `(バッククォート)や%xで囲めばJavaScriptをRubyの中に混在させられる
- RubyとJavaScriptを違和感なくつなぐには、各命令をラップするクラスが必要
といった感じです。
さて、RubyコードではなくなってしまうのでJavaScript混在は嫌ですが、各命令をラップする時間も(締め切り直前のため)ないので、困ってしまいました。仕方がないので黒魔術(ダーティハック)に手を出すことにします。暗黒プログラマ万歳!
以下、main.rb
冒頭の黒魔術コードです。
module P5 def self.method_missing(name, *args) %x{ obj = window[name]; if (typeof(obj) == 'function') { return window[name].apply(window, args); } else { return window[name]; } } end %x{ window.setup = function() { Opal.top.$setup(); }; window.draw = function() { Opal.top.$draw(); }; } end
前半のmethod_missing
定義部分では、未定義の名前が使用されたら関数であればJavaScriptのグローバル関数を実行し、変数であればグローバル変数を返すようにしています。こうすることで、P5.some_method
のような呼び出しがあると、JavaScriptのsome_method
が自動で呼ばれるようになります。
後半の%x
の部分では、JavaScriptのsetup
関数とdraw
関数が呼ばれたら、Rubyのトップレベルで定義されたsetup
メソッドとdraw
メソッドを呼び出すようにしています。
Rubyでスケッチを作成する
最後に動作確認用のサンプルを作成します。
ここでは手抜きをして、以前作成したスケッチをそのまま流用しました。p5.jsの命令は冒頭にP5.
が付いていることに注意してください。
LINE_RADIUS = 8 LINE_SPEED = 3 def setup P5.createCanvas(480, 240) P5.background(96) P5.noStroke @x = @y = LINE_RADIUS @vx = @vy = LINE_SPEED end def draw P5.fill(96, 8) P5.rect(0, 0, P5.width, P5.height) @x += @vx @y += @vy @vx *= -1 if @x <= LINE_RADIUS || @x >= P5.width - LINE_RADIUS @vy *= -1 if @y <= LINE_RADIUS || @y >= P5.height - LINE_RADIUS P5.fill(255, 204, 0) P5.ellipse(@x, @y, LINE_RADIUS * 2, LINE_RADIUS * 2) end
特に変わったコードは書いていないので、Rubyになじみのない方でも、Processingを知っていれば内容は理解できるのではないかと思います。
動作確認
実際の動作画面です。
また、こちらで実際に動く様子が確認できます。
- Processing in Ruby on Browser(CodePen用に全コードをhtmlに埋め込んでいます)
無事、Rubyで作成したスケッチをブラウザ上で動かすことができました。
まとめ
今回、かろうじてRubyからp5.jsを動かすことができましたが、未対応のコールバック等、色々穴があるので、やはりきちんとしたラッパーライブラリを作成しないと実用は難しいです。
年末にでも、正式なOpal向けp5ラッパーライブラリの作成にトライしてみようと思います。