11 chapters
Getting started
From installing to your first moving picture, one small step at a time.
01 / 11
Install and the first minute
Install the gem. You need Ruby 3.2 or newer, and nothing else.
Type asobi in a terminal. The first time, it writes a small sketch called play.rb and opens it in your browser. This is the sketch:
# Change anything and save. The picture follows.
flower = petal.repeat(8).in_fan.colored(:crimson, :navy)
show flower.spin(every: 8)
Open in playground
Words in this step
show
Open play.rb in any editor, change a number and save. The picture in the browser changes by itself. Try 8 to 12.
flower = petal.repeat(12).in_fan.colored(:crimson, :navy)
show flower.spin(every: 8)
Open in playground
Press Ctrl-C in the terminal to stop. Later you can run any file with asobi file.rb or ruby file.rb.
02 / 11
Make a shape
A shape word makes a picture. show puts it on the screen. That is all you need for a first picture.
show circle
Open in playground
Every number can be left out, so it just works. When you want to decide, give the number a name.
show circle(radius: 30)
Open in playground
There are many shapes. Try them one by one: rect, triangle, star, heart, moon, cloud.
show star(points: 7, radius: 30)
Open in playground
A picture is a value, so you can keep it in a variable. Think of it as a sticker: you make it once, and then you can ask it to do things.
dot = circle(10)
show dot
Open in playground
Lengths have no unit, and the picture is fitted to the screen. Only the proportions matter.
03 / 11
Make more of it
repeat copies a picture. Here are five circles. They all lie in the same place, so you see only one.
show circle.repeat(5)
Open in playground
Words in this step
repeat
Now put them in a row. Copying and arranging are two separate steps.
show circle.repeat(5).in_row
Open in playground
Words in this step
in_row
* is the short way to write repeat. It is one of the only two symbols in asobi.
dots = circle * 5
show dots.in_row(gap: 6)
Open in playground
Words in this step
*
04 / 11
Arrange it
in_ring puts the copies evenly round a circle. You did not say how big the ring is, and it still works: it picks a size where the neighbours do not touch.
show circle(5).repeat(12).in_ring
Open in playground
Words in this step
in_ring
in_grid makes a table. The number of columns can be written without its name.
show square.repeat(12).in_grid(4, gap: 4)
Open in playground
Words in this step
in_grid
A petal starts at the centre and grows straight up. Copy it and turn each copy a little more with in_fan: that is a flower.
show petal.repeat(8).in_fan
Open in playground
More ways to arrange: in_spiral, in_row, in_column, and scattered, which puts things in random places. A scattered picture is different every time you run it, but it does not jump around when you save.
show circle(3).repeat(150).in_spiral
Open in playground
To change each copy a little differently, use change_each. The block gets the picture, t (0.0 for the first, up to nearly 1.0 for the last) and the number.
dots = circle(5).repeat(12).change_each { |dot, t| dot.grow(to: 1 + t * 2) }
show dots.in_row(gap: 4)
Open in playground
Words in this step
change_each
grow
05 / 11
Add colour
colored paints. A colour is a CSS colour name written as a symbol: :crimson, :navy, :skyblue, :gold. With no name at all, you get crimson.
show heart.colored(:crimson)
Open in playground
Words in this step
colored
Give a group several colours and they are used in turn.
show petal.repeat(8).in_fan.colored(:crimson, :navy)
Open in playground
For a rainbow, use hsl. The hue is an angle from 0 to 360, and t in change_each goes from 0 up to 1, so the hue goes once round.
dots = circle(8).repeat(12).change_each { |dot, t| dot.colored(hsl(t * 360, 80, 55)) }
show dots.in_ring
Open in playground
Words in this step
hsl
outlined keeps only the line. stroke keeps the fill and adds a line. opacity makes it see-through.
show star(radius: 30).colored(:gold).stroke(:orange, width: 3)
Open in playground
The page is white by default. Give show a background with on:.
show star(radius: 30).colored(:gold), on: :midnightblue
Open in playground
06 / 11
Put pictures together
on puts one picture on top of another.
show circle(8).colored(:gold).on(circle(20).colored(:navy))
Open in playground
Words in this step
on
+ puts pictures on top of each other too. What you write later is in front. It is the other symbol in asobi.
sun = circle(30).colored(:gold)
show sun + circle(14).colored(:orange) + circle(5).colored(:crimson)
Open in playground
beside puts pictures side by side, and above stacks them. The result is again one picture.
show circle.beside(square).above(triangle)
Open in playground
That is the point: whatever you build is one picture, so you can copy it, arrange it and paint it again. Here is a face, built from smaller pictures.
eyes = circle(3).repeat(2).in_row(gap: 12)
smile = arc(10, from: 90, to: 270).outlined(:black, width: 2)
face = eyes.above(smile, gap: 6).on(circle(24).colored(:gold))
show face.repeat(4).in_row(gap: 6)
Open in playground
Words in this step
arc
07 / 11
Ask objects to be pictures
Any object can be asked to become a picture with to_art. A number is a circle of that radius. An array is a row of pictures.
show [4, 8, 12, 16].to_art
Open in playground
Words in this step
to_art
Numbers do not have to be circles. Say what shape they should be with as:, and the numbers become sizes.
fib = [1, 1, 2, 3, 5, 8, 13]
show fib.to_art(as: star).colored(:gold, :orange), on: :black
Open in playground
Strings become letters. chars splits a word into one letter each, so each letter is its own picture you can colour.
show "asobi".chars.to_art.colored(:crimson, :navy)
Open in playground
Words in this step
text
An array of arrays is a table: each inner array is a row.
table = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
show table.to_art(as: square).colored(:teal, :navy)
Open in playground
show asks for to_art for you, so for a plain list of numbers you can leave it out. To paint or move it, ask first.
show [3, 6, 9, 12]
Open in playground
08 / 11
Make it move
A motion word turns a picture into a picture in motion. every is how many seconds one loop takes.
show star(radius: 30).colored(:gold).spin(every: 3)
Open in playground
Words in this step
spin
Motion words follow each other. The words you already know, like colored, work on motions too.
flower = petal.repeat(8).in_fan.colored(:crimson, :navy)
show flower.spin(every: 8).pulse(every: 1.5)
Open in playground
Words in this step
pulse
sway
bounce
orbit
fade
drift
shift_color
one_by_one makes a group move one at a time instead of all together. Each dot pulses around its own centre, so the ring keeps its shape.
dots = circle(5).repeat(24).in_ring.colored(:crimson, :orange)
show dots.pulse(every: 2, to: 2).one_by_one
Open in playground
Words in this step
one_by_one
one_by_one belongs right after the motion word it staggers. To stagger two motions, write it after each. Here the colour and the bounce both go one by one.
hearts = heart(20).repeat(5).in_row(gap: 6)
show hearts.shift_color(:crimson, :orchid).one_by_one.bounce(height: 24).one_by_one
Open in playground
A moving picture and a still one go together: still on moving gives a motion.
sun = circle(10).colored(:gold)
planet = circle(4).colored(:skyblue).orbit(radius: 30, every: 5)
show sun + planet, on: :black
Open in playground
For your own motion, animate gives you the picture and t, which runs from 0.0 up to 1.0 and starts again.
show star(radius: 20).animate(4) { |picture, t| picture.rotate(t * 360).grow(to: 1 + t) }
Open in playground
Words in this step
animate
09 / 11
Make it glow
So far everything was flat: plain colours, like ink on white paper. That is the right place to start. Richness is something you add, one word at a time. Here is a flat flower on a dark page.
flower = petal(40, 16).repeat(8).in_fan.colored(:hotpink, :gold)
show flower, on: :black
Open in playground
Add glowing. The edges glow in the picture's own colour. A glow needs a dark page, which is why we chose black.
flower = petal(40, 16).repeat(8).in_fan.colored(:hotpink, :gold)
show flower.glowing, on: :black
Open in playground
blended chooses how a picture mixes with what is under it. With :screen (the default), lights add up: where the circles overlap, the colour gets lighter. It shows only because something is underneath, here the other circle and the black page.
red = circle(30).colored(:crimson).move(left: 14)
blue = circle(30).colored(:dodgerblue).move(right: 14)
show red.blended + blue.blended, on: :black
Open in playground
Words in this step
blended
colored can also make a gradient. Name where it starts and ends. On a group, the gradient flows once across the whole group, and around: true runs it from the centre outwards.
petals = petal(50, 18).repeat(8).in_fan.colored(from: :hotpink, to: :gold, around: true)
show petals.glowing, on: :black
Open in playground
Words in this step
colored
trail leaves fading copies of earlier moments behind a moving picture. It does nothing to a still picture.
comet = circle(5).colored(:gold).orbit(radius: 30, every: 3).trail(10)
show comet.glowing, on: :black
Open in playground
Words in this step
trail
Last, grain: true in show lays a fine film grain over the whole picture, like paper or old film. It is a way of showing, so it is written in show, not on the picture.
comet = circle(5).colored(:gold).orbit(radius: 30, every: 3).trail(10)
show comet.glowing, on: :black, grain: true
Open in playground
On a light page, use shadowed to make pictures look like cut-out paper lying on top of each other.
sun = circle(22).colored(:gold).move(up: 20, right: 40)
hill = ellipse(220, 70).colored(:seagreen).move(down: 50)
show sun.shadowed + hill.shadowed, on: :oldlace
Open in playground
Words in this step
shadowed
And clipped_by shows a picture only through the window of a shape. Here, stripes seen through a heart.
stripes = rect(10, 80).repeat(10).in_row.colored(:crimson, :gold)
show stripes.clipped_by(heart(60))
Open in playground
Words in this step
clipped_by
10 / 11
Show it live, and record it
When you run a sketch, the browser opens and watches the file. Every time you save, the picture is swapped for the new one, and the motion carries on from where it was.
show circle(30).colored(:crimson).pulse
Open in playground
If you make a typing mistake, nothing closes. The last good picture stays on the screen, and the error is written below it, with a hint on how to fix it. Fix it and save.
Move the mouse and small controls appear at the edge for a few seconds. Press c to lay the code over the picture. Press r to start recording, and r again to stop: the browser saves a video file.
show can also take a background (on:) and a paper size (paper:).
show star(radius: 30).colored(:gold).spin(every: 4), on: :black, paper: [400, 400]
Open in playground
To keep a picture, use save. A still picture becomes an SVG file. A motion becomes an HTML page that plays by itself. (Remove the # to try it.)
flower = petal.repeat(8).in_fan.colored(:crimson, :navy)
# flower.save("flower.svg")
# flower.spin(every: 8).save("flower.html")
show flower.spin(every: 8)
Open in playground
Words in this step
save
11 / 11
Make your own class a picture
Here is the idea of asobi: treat objects as pictures, and pictures as objects. Write a method called to_art in your class and say what the object looks like.
class Flower
def initialize(petals, color)
@petals = petals
@color = color
end
def to_art
petal(30, 12).repeat(@petals).in_fan.colored(@color)
end
end
show Flower.new(8, :crimson)
Open in playground
Now your objects can go anywhere a picture can: in an array, beside another picture, on top of one.
class Flower
def initialize(petals, color)
@petals = petals
@color = color
end
def to_art
petal(30, 12).repeat(@petals).in_fan.colored(@color)
end
end
garden = [Flower.new(5, :crimson), Flower.new(8, :navy), Flower.new(12, :gold)]
show garden.to_art
Open in playground
To call picture words directly on your objects, like flower.spin, add one line to the class: include Asobi::Art. It works like Enumerable: you write each, and you get map, select and the rest. Here you write to_art, and you get spin, move, colored and the rest.
class Flower
include Asobi::Art
def initialize(petals, color)
@petals = petals
@color = color
end
def to_art
petal(30, 12).repeat(@petals).in_fan.colored(@color)
end
end
show Flower.new(8, :navy).spin(every: 6)
Open in playground
Anything can be a picture. A class can hold numbers, names, dates, anything, and decide for itself how to look. Next, look at the gallery, or open the reference to see every word.