asobi

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

Words in this step circle show

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

Words in this step star heart rect triangle moon cloud

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

Words in this step petal in_fan

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

Words in this step in_spiral in_column scattered

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

Words in this step outlined stroke opacity

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

Words in this step + under

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

Words in this step beside above

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

Words in this step glowing blurred

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.