Week 2 - L5 Overview and A Random Walk

Date: 2026-08-31

An iceberg’s meandering path Image uploaded to Are.na by Jon-Kyle Mohr

Today

Lua!

Set up L5

Download and step-by-step instructions

There are full step-by-step download instructions for Mac, Win, Linux

A basic L5 sketch

--this is a comment

function setup()
  size(500,500)
end

function draw()
  --main loop
  --each run of draw is a frame
end

variables

Lua is a dynamically typed language. Variables themselves don’t have types. Instead, the values themselves do.

By default, variables are global-scoped, unless set to be local.

require('L5')

function setup()
  x = 0
  local y = 0
end

function draw()
  print(x) -- prints 0
  print(y) -- nil
end

event functions

We covered setup and draw. There are also additional event functions:

Working with media

function setup()
  --load media here
  img = loadImage('assets/quality_meme.jpg')
end

Reference example - loading an image

Input

ellipse(mouseX,mouseY,20)

Keypresses, mouse movement and clicks are examples of input and interaction

Looping

Similar to Processing/Java

for i = 1,10 do
  print(i)    -- will print 1, 2, 3...to 10 on separate lines
end

You can optionally add an increment value

for bottle = 99,1,-1 do
  print(bottle..'bottles of NA beverages on the wall..')
end

Tables

The main data structure in Lua is the table, which is used to create all other needed formats.

For example…

arrays

Arrays can be described literally. They start with 1.

The length of an array is returned through adding a # before the array name.

array = {'first','second','last'}

print(array[1]) -- 'first'
print(#array) -- 3

Add to the end of an array

local numArray = {1, 2, 3}

table.insert(numArray, 4}
table.insert(numArray, 5}
--will result in [1, 2, 3, 4, 5]

Remove from end of array

--continuing from above
table.remove(numArray) --will remove from end
--will result [1, 2, 3, 4]

Looping through an array

Lua has the utilities pairs and ipairs. We can optionally use ipairs when looping through an array.

Without ipairs:

for i=1,#students do
  print(students[i])
end

With ipairs:

for student in ipairs(students) do
  print(student)
end

Debugging and the console

Accessing the Console

Error messages and print messages are printed in the command line.

They can also be presented onscreen through use of the printToScreen() function added in your setup.

Next steps:

Icebergs and Random Walks

Satellite video of world’s biggest iceberg, A23a, breaking free

from NPR

Iceberg A68a
from SciTechHub

When icebergs break away from ice shelves or large glacier fronts, they become travelers in the ocean, carried by currents, spinning in eddies, shifting with the tides, and pushed along by the wind. Sometimes, these massive ice chunks get stuck — either grounded on a shallow seafloor or caught in a swirling mass of water. Iceberg A-23A experienced both. While every iceberg’s journey is unique, most follow the same general path. More than 90 percent of bergs around Antarctica enter the clockwise-flowing current of the Weddell Gyre off East Antarctica and eventually escape, shooting north along the Antarctic Peninsula and finally out across the Drake Passage into warmer South Atlantic waters—an ocean route known as “iceberg alley.” –How the World’s Largest Iceberg Escaped an Ocean Whirlpool, from SciTechDaily

Evan Roth’s Heliographs

Heliographs by Evan Roth

The path of light on the sun, an embroidery Image from Dorothée Nilsson Gallery

During a residency at the Astronomical Observatory of the University of Namur, Belgium, Roth worked with astronomers imaging the surface of the Sun through a telescope fitted with an infrared camera, a lens that registers wavelengths beyond human vision, revealing what remains otherwise invisible to the naked eye. The camera produces greyscale images, which Roth then colourises intuitively, treating colour as a painterly choice. The result is a hybrid: photograph, quilt, astronomical data, and painting at once. These solar photographs are digitally printed onto fabric and stitched with a sewing machine following a pattern known as a random walk: a phenomenon in astrophysics in which a photon born in the Sun’s core takes, on average, one hundred thousand years to reach its surface, drifting through solar plasma in a slow, unpredictable arc, before crossing the distance to Earth in 8 minutes and 20 seconds. For Roth, it is both concept and method: a way of reanimating the photon’s journey in thread, and of enacting the wandering, drifting attention that slow transformation requires. Exhibition Text: Evan Roth

Perlin Noise

Returns random numbers that can be tuned to feel organic.

Values returned by random() and randomGaussian() can change by large amounts between function calls. By contrast, values returned by noise() can be made “smooth”. Calls to noise() with similar inputs will produce similar outputs. noise() is used to create textures, motion, shapes, terrains, and so on. Ken Perlin invented noise() while animating the original Tron film in the 1980s. The version of noise in L5 varies from p5.js and Processing. It returns simplex noise for 1 and 2 input arguments. Simplex noise is an algorithm designed in 2001 by Ken Perlin to address limitations in his classic noise function, notably relating to speed, complexity and higher order dimensions. noise() always returns values between 0 and 1. It returns the same value for a given input while a sketch is running. noise() produces different results each time a sketch runs. The character of the noise can be adjusted by scaling the inputs. noise() interprets inputs as coordinates. The sequence of noise values will be smoother when the input coordinates are closer. The version of noise() with one parameter computes noise values in one dimension. This dimension can be thought of as space, as in noise(x), or time, as in noise(t). The version of noise() with two parameters computes noise values in two dimensions. These dimensions can be thought of as space, as in noise(x, y), or space and time, as in noise(x, t).

Important: The output of noise() might be constant if only using integers as arguments. Be sure to use floats to get varying return values.

from noise in the L5 reference

Noise in L5: example code

noise example 1
require("L5")

function setup()
  size(100, 100)

  describe('A black dot moves randomly on a gray square.')
end

function draw()
  background(200)

  -- Calculate the coordinates.
  local x = 100 * noise(0.005 * frameCount)
  local y = 100 * noise(0.005 * frameCount + 10000)

  -- Draw the point.
  strokeWeight(5)
  point(x, y)
end
noise example 2
require("L5")

function setup()
  size(100, 100)

  describe('A black dot moves randomly on a gray square.')
end

function draw() 
  background(200)

  -- Set the noise level and scale.
  local noiseLevel = 100
  local noiseScale = 0.005

  -- Scale the input coordinate.
  local nt = noiseScale * frameCount

  -- Compute the noise values.
  local x = noiseLevel * noise(nt)
  local y = noiseLevel * noise(nt + 10000);

  -- Draw the point.
  strokeWeight(5)
  point(x, y)
end
noise example 3
require("L5")

function setup()
  size(100, 100)

  describe('A hilly terrain drawn in gray against a black sky.')
end

function draw() 
  -- Set the noise level and scale.
  local noiseLevel = 100
  local noiseScale = 0.02

  -- Scale the input coordinate.
  local x = frameCount
  local nx = noiseScale * x

  -- Compute the noise value.
  local y = noiseLevel * noise(nx)

  -- Draw the line.
  line(x, 0, x, y)
end
noise example 4
require("L5")

function setup()
  size(100, 100)

  describe('A calm sea drawn in gray against a black sky.')
end

function draw() 
  background(200)

  -- Set the noise level and scale.
  local noiseLevel = 100;
  local noiseScale = 0.002

  -- Iterate from left to right.
  for x = 1,width do
    -- Scale the input coordinates.
    local nx = noiseScale * x
    local nt = noiseScale * frameCount

    -- Compute the noise value.
    local y = noiseLevel * noise(nx, nt)

    -- Draw the line.
    line(x, 0, x, y)
  end
end
noise example 5
require("L5")

function setup()
  size(100, 100)

  background(200)

  -- Set the noise level and scale.
  local noiseLevel = 255
  local noiseScale = 0.01

  -- Iterate from top to bottom.
  for y=1,height do
    -- Iterate from left to right.
    for x = 0,width do
      -- Scale the input coordinates.
      local nx = noiseScale * x
      local ny = noiseScale * y

      -- Compute the noise value.
      local c = noiseLevel * noise(nx, ny)

      -- Draw the point.
      stroke(c)
      point(x, y)
    end
  end

  describe('A gray cloudy pattern.')
end
noise example 6
require("L5")

function setup()
  size(100, 100)

  describe('A gray cloudy pattern that changes.')
end

function draw() 
  -- Set the noise level and scale.
  local noiseLevel = 255
  local noiseScale = 0.009

  -- Iterate from top to bottom.
  for y=1,height do
    -- Iterate from left to right.
    for x=1,width do
      -- Scale the input coordinates.
      local nx = noiseScale * x
      local ny = noiseScale * y
      local nt = noiseScale * frameCount

      -- Compute the noise value.
      local c = noiseLevel * noise(nx, ny, nt)

      -- Draw the point.
      stroke(c)
      point(x, y)
    end
  end
end

Perlin noise videos

How to Code Procedural Terrain with Perlin Noise (JavaScript & p5.js) with RachelfTech

link to code repo

Perlin Noise in p5.js with The Coding Train

Workshop

There are a few approaches. Pick one, blend a few, or invent your own:

Parameters:

Uses at least one of: random walk, Perlin noise, or both together.

Flow-field and particle-system techniques are widely documented in creative-coding tutorials; you’re welcome to learn from them, but your submitted piece should be your own composition and combination of ideas, not a recreation of a tutorial’s exact result