I have just released a new version of JRubyArt, which uses OpenSimplexNoise2 to implement noise, I was partly persuaded to convert from processing noise implementation (not really Perlin noise) by the work of Etienne Jacob, who’s work is worth looking out. Here is one of his tutorial examples converted to JRubyArt.

```
# After a tutorial exmple by Etienne Jacob https://bleuje.github.io/tutorial4/
WEB = %w[#f7f7f7 #cceabb #3f3f44 #fdcb9e]
NUM_FRAMES = 70
K = 130
SCL = 120.0
attr_reader :palette
def settings
size(500, 500)
end
def setup
sketch_title 'Replacement Technique'
@palette = web_to_color_array(WEB)
end
def random_value(p, seed)
((noise(SCL * p, seed) + 1) / 2.0)**2
end
def position(p)
q = sqrt(p)
theta = 7 * TWO_PI * q
r = 0.4 * width * q
Vec2D.new(r * cos(theta), r * sin(theta))
end
def draw_thing(p)
v1 = random_value(p, 100)
v2 = random_value(p, 200)
v3 = random_value(p, 300)
sumv = v1 + v2 + v3
alpha = 10 * TWO_PI * p
theta0 = alpha + 0
theta1 = alpha + map1d(v1 / sumv, 0..1, 0..TWO_PI)
theta2 = alpha + map1d((v1 + v2) / sumv, 0..1, 0..TWO_PI)
theta3 = alpha + TWO_PI
radius = 30 * sin(PI * p) # sin(PI*p) is 0 at p=0 and p=1 and 1 in the middle
pos = position(p)
no_stroke
fill(palette[1])
arc(pos.x, pos.y, radius, radius, theta0, theta1)
fill(palette[2])
arc(pos.x, pos.y, radius, radius, theta1, theta2)
fill(palette[3])
arc(pos.x, pos.y, radius, radius, theta2, theta3)
end
def draw
background(palette[0])
t = 1.0 * frame_count / NUM_FRAMES
push_matrix
translate(width / 2, height / 2)
K.times do |i|
p = 1.0 * (i + t) / K
draw_thing(p)
end
pop_matrix
save_frame(data_path('fr###.png')) if frame_count <= NUM_FRAMES
return unless frame_count == NUM_FRAMES
puts('All frames have been saved')
stop
end
```

Here’s a 4D Noise example:-

```
Coord = Struct.new(:mx, :my, :mz, :az, :al)
attr_reader :half_w, :half_h, :radius, :spin_x, :spin, :coords
def settings
size(480, 480, P3D)
end
def setup
sketch_title '4D Simplex Noise Test'
background(0)
stroke(255)
fill(32, 255, 64)
@half_w = width * 0.5
@half_h = height * 0.5
@radius = height * 0.4
@spin_x = 0.0
@spin = 0.0
@smth = false
angle = ((1.0 + Math.sqrt(5)) / 2.0 - 1) * TAU # Fibonacci distribution
@coords = (0..2_000).map do |i|
inc = Math.asin(i / 1_000.0 - 1.0) # inclination
az = angle * i # azimuth
# Too lazy to do this the right way... precalculating both the angles and the coordinates
Coord.new.tap do |coord|
push_matrix
rotate_y(az)
rotate_z(inc)
translate(radius, 0, 0)
coord.mx = g.model_x(0, 0, 0) * 0.007
coord.my = g.model_y(0, 0, 0) * 0.007
coord.mz = g.model_z(0, 0, 0) * 0.007
coord.az = az
coord.al = inc
pop_matrix
end
end
end
def draw
background(0)
@spin -= (mouse_x - pmouse_x) * 0.0001 if mouse_pressed?
@spin_x += spin
@spin *= 0.98
push_matrix
translate(half_w, half_h, -0)
rotate_y(-spin_x)
coords.each do |ci|
push_matrix
rotate_y(ci.az)
rotate_z(ci.al)
translate(radius, 0, 0)
dst = (g.model_z(0, 0, 0) + half_h) / 2 + 32
stroke(dst, dst * 0.5, dst * 0.25)
# 4D Simplex noise(x, y, z, time)
ang = noise(ci.mx, ci.my, ci.mz, frame_count * 0.007) * TAU
rotate_x(ang)
line(0, 0, 0, 0, 15, 0)
translate(0, 15, 0)
rotate_x(-10)
line(0, 0, 0, 0, 4, 0)
rotate_x(20)
line(0, 0, 0, 0, 4, 0)
pop_matrix
end
pop_matrix
end
```