ruby.wasm + p5.js の組み合わせです。ほとんどの API は移植したので大体同じことができると思います。

https://p5rb.ongaeshi.me/
使い方
p5.rb を HTML に読みこめばすぐに使えます。
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@next/dist/browser.script.iife.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.5.0/lib/p5.js"></script>
<script type="text/ruby" src="p5.rb"></script>
<script type="text/ruby">
def setup
createCanvas(720, 400)
end
def draw
background(127)
noStroke()
0.step(height, 20) do |i|
fill(129, 206, 15)
rect(0, i, width, 10)
fill(255)
rect(i, 0, 10, height)
end
end
P5::init()
</script>
</head>
<body>
<main>
</main>
</body>
</html>
オンラインエディタ
https://p5rb.ongaeshi.me/editor からオンラインエディタが使えます。

作ったものはURLで共有できます。モバイルからも使えるのでまずはこちらを触ってみるのがおすすめです。
できたものは #p5rb タグを付けて共有していただけるとよろこびます。
実装メモ
ruby.wasm の JS::Object
経由で p5.js のメソッドやプロパティをブリッジしています。Ruby の method_missing を使うことで 100 行程度で p5.js の全ての関数やプロパティを呼び出しています。method_missiong すごい。
p5.rb#L217-L332
class JS::Object
def method_missing(sym, *args, &block)
ret = self[sym]
case ret.typeof
when "undefined"
str = sym.to_s
if str[-1] == "="
self[str.chop.to_sym] = args.first
return args.first
end
super
when "function"
self.call(sym, *args, &block).to_r
else
ret.to_r
end
end
def respond_to_missing?(sym, include_private)
return true if super
self[sym].typeof != "undefined"
end
def to_r
case self.typeof
when "number"
self.to_f
when "string"
self.to_s
else
self
end
end
end
$p5 = nil
def method_missing(sym, *args, &block)
return super unless $p5.respond_to?(:[])
ret = $p5[sym]
case ret.typeof
when "undefined"
super
when "function"
$p5.call(sym, *args, &block).to_r
else
ret.to_r
end
end
JS.eval("window.constructors = { p5: (...args) => new p5(...args) };")
module P5
Vector = JS.global[:p5][:Vector]
module_function
def init(query = "main", obj = self)
unless query.is_a?(String)
query, obj = "main", query
end
$p5&.remove()
$p5 = nil
sketch = ->(p5) {
$p5 = p5
init_method(obj, :preload)
init_method(obj, :setup)
init_method(obj, :draw)
init_event_method(obj, :mouseMoved)
init_event_method(obj, :mouseDragged)
init_event_method(obj, :mousePressed)
init_event_method(obj, :mouseReleased)
init_event_method(obj, :mouseClicked)
init_event_method(obj, :doubleClicked)
init_event_method(obj, :mouseWheel)
init_event_method(obj, :keyPressed)
init_event_method(obj, :keyReleased)
init_event_method(obj, :keyTyped)
}
container = JS.global.document.querySelector(query)
container.innerHTML = ""
JS.global.window.constructors.p5(sketch, container)
end
def init_method(obj, sym)
if obj.respond_to?(sym, true)
m = obj.method(sym)
$p5[sym] = ->() { m.call() }
end
end
def init_event_method(obj, sym)
if obj.respond_to?(sym, true)
m = obj.method(sym)
if m.parameters.count >= 1
$p5[sym] = ->(e) { m.call(e) }
else
$p5[sym] = ->(e) { m.call() }
end
end
end
end
おわりに
作ったものがあったら #p5rb タグを付けて放流してもらえたら嬉しいです。
全ての戻り値に to_r するのはやりすぎかもしれないけど、プロパティを Ruby の括弧無しメソッド呼び出しで呼べるのは本家でできてもいいのかもと思っています(後で PR 出してみよう)。