1 Comment

Opal.rb: Ruby in the Browser

I fell in love with the idea of writing Ruby code in my browser long ago. I promptly wrote that dream off as impossible and learned coffeescript. But while coffeescript makes javascript much nicer to work with, it still doesn’t flow as nicely as good ‘ol Ruby. Opal.rb attempts to solve this problem. Opal.rb is a Ruby-to-javascript, source-to-source compiler. Opal supports most of Ruby already, including classes, modules, blocks and method_missing. Check out their rubyspec progress.

Opal has been under development for a couple of years now by Adam Beynon. There is an active community of people working to improve Opal and build extension libraries like opal-jquery, opal-sprockets, and vienna. I’ve started my own little Opal project while writing this blog post. I ported a basic Pixi.js example to Ruby via Opal.

I plan on cleaning up and completing the Pixi.js wrapper, but that’s a post for another day. Give Opal.rb a try online!

Here’s the example code:

include PIXI
stage = Stage.new 0x66FF99
renderer = WebGLRenderer.new 400, 300
 
Native(`window.document.body`).appendChild renderer.view
 
texture = Texture.from_image "bunny.png"
bunny = Sprite.new texture
bunny.anchor = Point.new(0.5, 0.5)
bunny.position = Point.new(300, 150)
 
stage.add_child(bunny)
 
animate = proc do
  `requestAnimFrame(animate)`
  bunny.rotation += 0.1
  renderer.render stage
end
`requestAnimFrame(animate)`

and here’s the supporting wrapper code:

require 'opal'
# require 'opal-jquery'
module PIXI
  class Stage
    %x{
      #{self}._proto = window.PIXI.Stage.prototype, def = #{self}._proto;
      window.PIXI.Stage.prototype._klass = #{self};
    }
 
    def self.new(color)
      `new window.PIXI.Stage(color)`
    end
 
    def add_child(child)
      `#{self}.addChild(child)`
    end
  end
 
  class WebGLRenderer
    %x{
       #{self}._proto = window.PIXI.WebGLRenderer.prototype, def = #{self}._proto;
      window.PIXI.WebGLRenderer.prototype._klass = #{self};
    }
    def self.new(width, height)
      `new window.PIXI.WebGLRenderer(width, height)`
    end
 
    def render(stage)
      `self.render(stage)`
    end
 
    def view
      `self.view`
    end
  end
  class Texture
    %x{
       #{self}._proto = window.PIXI.Texture.prototype, def = #{self}._proto;
      window.PIXI.Texture.prototype._klass = #{self};
    }
    def self.from_image(name)
      `window.PIXI.Texture.fromImage(name)`
    end
  end
  class Sprite
    %x{
       #{self}._proto = window.PIXI.Sprite.prototype, def = #{self}._proto;
      window.PIXI.Sprite.prototype._klass = #{self};
    }
    def self.new(texture)
      `new window.PIXI.Sprite(texture)`
    end
    def anchor
      Point.new `#{self}.anchor.x`,`#{self}.anchor.y`
    end
    def rotation
      `self.rotation`
    end
    def rotation=(r)
      `self.rotation = r`
    end
    def anchor=(a)
      `self.anchor = a`
    end
    def position=(p)
      `self.position = p`
    end
  end
  class Point
    %x{
       #{self}._proto = window.PIXI.Point.prototype, def = #{self}._proto;
      window.PIXI.Point.prototype._klass = #{self};
    }
    def self.new(x,y)
      `new window.PIXI.Point(x,y)`
    end
  end
end

And here’s what it draws:

pixijs_opalrb