Once upon a time Karsten Schmidt contributed to processing, he created the impressive toxiclibs library, and even ran workshops. Since then he has moved on to even more esoteric thi.ngs. Here is my JRubyArt version of one of his toxiclibs workshop examples, that does without the use of the toxiclibs library, makes use of JRubyArt Vec2D (instead of toxiclibs version) and ruby-meta programming.

See also how I have managed to embed a scaleable video into my own github webpage with markdown.

mycelium.rb (JRubyArt Sketch)

# Simple recursive branching system inspired by mycelium growth
# After an original sketch by Karsten Schmidt
# https://github.com/learn-postspectacular/sac-workshop-2013/blob/master/Mycelium/Mycelium.pde
# translated to JRubyArt by Martin Prout 2018

load_library :branch

attr_reader :renderer, :root

def setup
  @renderer = AppRender.new(self)
  @root = Branch.new(
    self,
    Vec2D.new(0, height / 2),
    Vec2D.new(1, 0),
    10.0
  )
end

def draw
  background(0)
  root.run
  save_frame(data_path('#######.png'))
end

def settings
  full_screen
end

branch.rb :branch library

require 'forwardable'

# Here we use the JRubyArt Vec2D class, and not toxis Vec2D. We avoid using
# ToxicLibsSupport, by using our own AppRender to translate Vec2D to vertices.
# Further we use the power of ruby (metaprogramming) to make Branch enumerable
# and use Forwardable to define which enumerable methods we want to use.
class Branch
  include Enumerable
  extend Forwardable
  def_delegators(:@children, :<<, :each, :length)
  # variance angle for growth direction per time step
  THETA = Math::PI / 6
  # max segments per branch
  MAX_LEN = 100
  # max recursion limit
  MAX_GEN = 3
  # branch chance per time step
  BRANCH_CHANCE = 0.05
  # branch angle variance
  BRANCH_THETA = Math::PI / 3
  attr_reader :position, :dir, :path, :children, :xbound, :speed, :ybound, :app

  def initialize(app, pos, dir, speed)
    @app = app
    @position = pos
    @dir = dir
    @speed = speed
    @path = []
    @children = []
    @xbound = Boundary.new(0, app.width)
    @ybound = Boundary.new(0, app.height)
    path << pos
  end

  def run
    grow
    display
  end

  private

  # NB: use of both rotate! (changes original) rotate (returns a copy) of Vec2D
  def grow
    check_bounds(position + (dir * speed)) if path.length < MAX_LEN
    @position += (dir * speed)
    dir.rotate!(rand(-0.5..0.5) * THETA)
    path << position
    if (length < MAX_GEN) && (rand < BRANCH_CHANCE)
      branch_dir = dir.rotate(rand(-0.5..0.5) * BRANCH_THETA)
      self << Branch.new(app, position.copy, branch_dir, speed * 0.99)
    end
    each(&:grow)
  end

  def display
    app.begin_shape
    app.stroke(255)
    app.no_fill
    path.each { |vec| vec.to_vertex(app.renderer) }
    app.end_shape
    each(&:display)
  end

  def check_bounds(pos)
    dir.x *= -1 if xbound.exclude? pos.x
    dir.y *= -1 if ybound.exclude? pos.y
  end
end

# we are looking for excluded values
Boundary = Struct.new(:lower, :upper) do
  def exclude?(val)
    true unless (lower...upper).cover? val
  end
end

Scaled sketch as embedded video with controls