|
| 1 | +# Computational Poetry with L5 |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +In this tutorial, you'll learn how to create generative visual poetry using L5, a creative coding library based on p5.js and Processing, but written in Lua. We'll explore how code can arrange words and images to create dynamic compositions that change with every interaction. |
| 6 | + |
| 7 | +**Time Required:** 60 minutes |
| 8 | + |
| 9 | +**Prerequisites:** Previous knowledge working with p5.js or Processing is recommended. |
| 10 | + |
| 11 | +## Learning Objectives |
| 12 | + |
| 13 | +By the end of this tutorial, students will be able to: |
| 14 | + |
| 15 | +- Understand the basics of L5 and how it relates to p5.js/Processing |
| 16 | +- Draw text and shapes to the screen using code |
| 17 | +- Create and manipulate arrays of strings |
| 18 | +- Use randomness to create generative compositions |
| 19 | +- Build a grid-based layout system |
| 20 | +- Combine text, randomness, and interaction to create computational poetry |
| 21 | + |
| 22 | +## Setup |
| 23 | + |
| 24 | +To get started with L5, you'll need to set it up on your computer: |
| 25 | + |
| 26 | +- **Mac users:** [L5 Setup for Mac](https://l5lua.org/download/install-mac) |
| 27 | +- **Windows users:** [L5 Setup for Windows](https://l5lua.org/download/install-windows) |
| 28 | + |
| 29 | +Once you have L5 installed, you're ready to begin. |
| 30 | + |
| 31 | +## What is L5? |
| 32 | + |
| 33 | +L5 is a creative coding library that brings the simplicity and approachability of p5.js and Processing to the Lua programming language. If you've used p5.js or Processing before, you'll find L5 familiar - it uses the same basic structure with `setup()` and `draw()` functions, and similar function names for drawing shapes, working with color, and handling interaction. |
| 34 | + |
| 35 | +The main difference is that L5 uses Lua syntax, which is known for being clean and beginner-friendly. Lua uses simple keywords like `function`, `end`, and `for` that make code easy to read. |
| 36 | + |
| 37 | +## Getting Started: Your First L5 Sketch |
| 38 | + |
| 39 | +Every L5 sketch starts with requiring the library. Create a new file and type: |
| 40 | + |
| 41 | +```lua |
| 42 | +require 'L5' |
| 43 | + |
| 44 | +function setup() |
| 45 | + -- Your setup code goes here |
| 46 | +end |
| 47 | +``` |
| 48 | + |
| 49 | +The `setup()` function runs once when your program starts. This is where we'll put code that only needs to happen at the beginning. |
| 50 | + |
| 51 | +## Step 1: Drawing a Background |
| 52 | + |
| 53 | +Let's start by setting a background color. Add this inside your `setup()` function: |
| 54 | + |
| 55 | +```lua |
| 56 | +require 'L5' |
| 57 | + |
| 58 | +function setup() |
| 59 | + background('white') |
| 60 | +end |
| 61 | +``` |
| 62 | + |
| 63 | +Run your sketch. You should see a white canvas. Try changing `'white'` to other colors like `'lightblue'`, `'pink'`, or `'lavender'`. You can use any of the [HTML Color Names](https://www.w3schools.com/colors/colors_names.asp), or RGB numbers like `background(255, 0, 255)` or hexadecimal: `background('#7FFFD4')`. |
| 64 | + |
| 65 | +## Step 2: Drawing Basic Shapes |
| 66 | + |
| 67 | +L5 can draw shapes just like p5.js. Let's add a circle: |
| 68 | + |
| 69 | +```lua |
| 70 | +require 'L5' |
| 71 | + |
| 72 | +function setup() |
| 73 | + background('white') |
| 74 | + fill('black') |
| 75 | + circle(100, 100, 50) |
| 76 | +end |
| 77 | +``` |
| 78 | + |
| 79 | +The `fill()` function sets the color for shapes, and `circle(x, y, diameter)` draws a circle at position (100, 100) with a diameter of 50 pixels. |
| 80 | + |
| 81 | +Try drawing other shapes: |
| 82 | +- `rect(x, y, width, height)` - draws a rectangle |
| 83 | +- `ellipse(x, y, width, height)` - draws an ellipse |
| 84 | +- `line(x1, y1, x2, y2)` - draws a line |
| 85 | + |
| 86 | +## Step 3: Drawing Text on Screen |
| 87 | + |
| 88 | +Now let's draw some text instead of shapes: |
| 89 | + |
| 90 | +```lua |
| 91 | +require 'L5' |
| 92 | + |
| 93 | +function setup() |
| 94 | + background('white') |
| 95 | + fill('black') |
| 96 | + textSize(32) |
| 97 | + text('hello', 100, 100) |
| 98 | +end |
| 99 | +``` |
| 100 | + |
| 101 | +The `text()` function draws text at a specific position. The first parameter is the string to display, and the next two are the x and y coordinates. |
| 102 | + |
| 103 | +Try changing: |
| 104 | +- The word in quotes |
| 105 | +- The numbers (position) |
| 106 | +- The `textSize()` value |
| 107 | +- Add `textAlign(CENTER, CENTER)` before the text to change alignment |
| 108 | + |
| 109 | +## Step 4: Creating an Array of Strings |
| 110 | + |
| 111 | +Instead of just one word, let's create a collection of words we can choose from. In Lua, we use curly braces `{}` to create arrays (called "tables" in Lua): |
| 112 | + |
| 113 | +```lua |
| 114 | +require 'L5' |
| 115 | + |
| 116 | +words = {"eye", "nose", "mouth", "ear", "brain"} |
| 117 | + |
| 118 | +function setup() |
| 119 | + background('white') |
| 120 | + fill('black') |
| 121 | + textSize(32) |
| 122 | + text(words[1], 100, 100) |
| 123 | +end |
| 124 | +``` |
| 125 | + |
| 126 | +The array `words` holds multiple strings. We can access them using square brackets with a number. **Important:** Lua arrays start at index 1, not 0! |
| 127 | + |
| 128 | +Try displaying different words by changing `words[1]` to `words[2]`, `words[3]`, etc. |
| 129 | + |
| 130 | +## Step 5: Picking a Random String |
| 131 | + |
| 132 | +Instead of always showing the same word, let's pick one randomly: |
| 133 | + |
| 134 | +```lua |
| 135 | +require 'L5' |
| 136 | + |
| 137 | +words = {"eye", "nose", "mouth", "ear", "brain"} |
| 138 | + |
| 139 | +function setup() |
| 140 | + background('white') |
| 141 | + fill('black') |
| 142 | + textSize(32) |
| 143 | + text(random(words), 100, 100) |
| 144 | +end |
| 145 | +``` |
| 146 | + |
| 147 | +The `random()` function can pick a random item from an array when you pass it an array. Run your sketch multiple times - you should see different words! |
| 148 | + |
| 149 | +## Step 6: Creating a Grid |
| 150 | + |
| 151 | +Now we'll use loops to create a grid of positions where words could appear. We'll use two variables to control the grid spacing: |
| 152 | + |
| 153 | +```lua |
| 154 | +require 'L5' |
| 155 | + |
| 156 | +words = {"eye", "nose", "mouth", "ear", "brain"} |
| 157 | + |
| 158 | +function setup() |
| 159 | + background('white') |
| 160 | + blockW = width/10 |
| 161 | + blockH = height/10 |
| 162 | + |
| 163 | + fill('black') |
| 164 | + textSize(24) |
| 165 | + |
| 166 | + for y=1,height,blockH do |
| 167 | + for x=1,width,blockW do |
| 168 | + text(random(words), x, y) |
| 169 | + end |
| 170 | + end |
| 171 | +end |
| 172 | +``` |
| 173 | + |
| 174 | +Let's break down the loop: |
| 175 | + |
| 176 | +- `for y=1,height,blockH do` means "start at 1, go up to the canvas height, increasing by blockH each time" |
| 177 | +- The nested loop does the same for x |
| 178 | +- `width` and `height` are built-in variables that give you the canvas dimensions |
| 179 | + |
| 180 | +This creates a grid of words across your entire canvas. |
| 181 | + |
| 182 | +## Step 7: Randomly Showing or Hiding Words |
| 183 | + |
| 184 | +Right now we're showing a word at every grid position. Let's make it more interesting by only sometimes showing a word: |
| 185 | + |
| 186 | +```lua |
| 187 | +require 'L5' |
| 188 | + |
| 189 | +words = {"eye", "nose", "mouth", "ear", "brain", "arm", "leg", "head", "foot"} |
| 190 | + |
| 191 | +function setup() |
| 192 | + background('white') |
| 193 | + blockW = width/10 |
| 194 | + blockH = height/10 |
| 195 | + |
| 196 | + fill('black') |
| 197 | + textAlign(RIGHT, BOTTOM) |
| 198 | + textSize(24) |
| 199 | + |
| 200 | + for y=1,height,blockH do |
| 201 | + for x=1,width,blockW do |
| 202 | + if random() > 0.9 then |
| 203 | + text(random(words), x, y) |
| 204 | + end |
| 205 | + end |
| 206 | + end |
| 207 | +end |
| 208 | +``` |
| 209 | + |
| 210 | +The `if random() > 0.9 then` line checks if a random number (between 0 and 1) is greater than 0.9. This means only about 10% of the grid positions will show a word. |
| 211 | + |
| 212 | +Try changing `0.9` to different values: |
| 213 | +- `0.5` = 50% of positions show words |
| 214 | +- `0.7` = 30% of positions show words |
| 215 | +- `0.95` = only 5% of positions show words |
| 216 | + |
| 217 | +## Step 8: Adding Interaction |
| 218 | + |
| 219 | +Let's make it so clicking the mouse regenerates the composition: |
| 220 | + |
| 221 | +```lua |
| 222 | +require 'L5' |
| 223 | + |
| 224 | +words = {"eye", "nose", "mouth", "ear", "brain", "arm", "leg", "head", "foot"} |
| 225 | + |
| 226 | +function setup() |
| 227 | + background('white') |
| 228 | + blockW = width/10 |
| 229 | + blockH = height/10 |
| 230 | + |
| 231 | + fill('black') |
| 232 | + textAlign(RIGHT, BOTTOM) |
| 233 | + textSize(24) |
| 234 | + |
| 235 | + for y=1,height,blockH do |
| 236 | + for x=1,width,blockW do |
| 237 | + if random() > 0.9 then |
| 238 | + text(random(words), x, y) |
| 239 | + end |
| 240 | + end |
| 241 | + end |
| 242 | +end |
| 243 | + |
| 244 | +function mousePressed() |
| 245 | + setup() |
| 246 | +end |
| 247 | +``` |
| 248 | + |
| 249 | +The `mousePressed()` function runs whenever you click the mouse. By calling `setup()` again, we redraw everything with new random positions! |
| 250 | + |
| 251 | +## Making It Your Own |
| 252 | + |
| 253 | +Now that you have the basic structure, try customizing it: |
| 254 | + |
| 255 | +1. **Change the words:** Replace the body parts with your own theme |
| 256 | + - Emotions: "joy", "anger", "fear", "sadness" |
| 257 | + - Colors: "crimson", "azure", "golden", "silver" |
| 258 | + - Use words from a poem you wrote |
| 259 | + |
| 260 | +2. **Adjust the density:** Change the `0.9` value to make it more or less crowded |
| 261 | + |
| 262 | +3. **Experiment with text alignment:** Try `LEFT`, `RIGHT`, `CENTER` for horizontal and `TOP`, `CENTER`, `BOTTOM` for vertical |
| 263 | + |
| 264 | +4. **Change the grid:** Make `blockW` and `blockH` smaller for a finer grid, or larger for bigger gaps |
| 265 | + |
| 266 | +5. **Add color:** Try `fill('red')` or use RGB values like `fill(255, 0, 0)` |
| 267 | + |
| 268 | +## Extension Ideas |
| 269 | + |
| 270 | +Ready to go further? Try these challenges: |
| 271 | + |
| 272 | +### Adding Animation |
| 273 | + |
| 274 | +Instead of only drawing in `setup()`, you can use the `draw()` function to create continuous animation: |
| 275 | + |
| 276 | +```lua |
| 277 | +function draw() |
| 278 | + background('white') |
| 279 | + -- your grid code here |
| 280 | +end |
| 281 | +``` |
| 282 | + |
| 283 | +This will create a constantly changing composition! |
| 284 | + |
| 285 | +### Using Mouse Interaction |
| 286 | + |
| 287 | +Make your composition respond to the mouse position: |
| 288 | + |
| 289 | +```lua |
| 290 | +textSize(mouseX / 10) -- Text size changes with mouse X position |
| 291 | +``` |
| 292 | + |
| 293 | +This can cause freezing if mouseX is 0 at the beginning of the sketch, so we can constrain it to have a minimum value. |
| 294 | + |
| 295 | +```lua |
| 296 | +textSize(constrain(mouseX,10,width) / 10) |
| 297 | +``` |
| 298 | + |
| 299 | +Now the size will change but never get smaller than 10. |
| 300 | + |
| 301 | +Or only show words near the mouse: |
| 302 | + |
| 303 | +```lua |
| 304 | +if dist(x, y, mouseX, mouseY) < 100 then |
| 305 | + text(random(words), x, y) |
| 306 | +end |
| 307 | +``` |
| 308 | + |
| 309 | +### Adding Images |
| 310 | + |
| 311 | +L5 can also work with images. First, load an image in `setup()`: |
| 312 | + |
| 313 | +```lua |
| 314 | +img = loadImage('path/to/your/image.jpg') |
| 315 | +``` |
| 316 | + |
| 317 | +Then draw it in your grid instead of (or alongside) text: |
| 318 | + |
| 319 | +```lua |
| 320 | +if random() > 0.9 then |
| 321 | + if random() > 0.5 then |
| 322 | + text(random(words), x, y) |
| 323 | + else |
| 324 | + image(img, x, y, 50, 50) |
| 325 | + end |
| 326 | +end |
| 327 | +``` |
| 328 | + |
| 329 | +### Rotating Text |
| 330 | + |
| 331 | +Add rotation to make text appear at different angles: |
| 332 | + |
| 333 | +```lua |
| 334 | +push() |
| 335 | +translate(x, y) |
| 336 | +rotate(random() * TWO_PI) |
| 337 | +text(random(words), 0, 0) |
| 338 | +pop() |
| 339 | +``` |
| 340 | + |
| 341 | +## Vocabulary |
| 342 | + |
| 343 | +- **Generative:** Art or designs created by following a set of rules or algorithms, often involving randomness |
| 344 | +- **Computational Poetry:** Using code and algorithms to create, arrange, or manipulate text as a form of poetic expression |
| 345 | +- **Array/Table:** A collection of values stored together (in Lua, called a "table") |
| 346 | +- **Algorithm:** A set of step-by-step instructions to solve a problem or create something |
| 347 | +- **Parameter:** A value you pass into a function to customize how it works |
| 348 | +- **Random:** Unpredictable; choosing something without a pattern |
| 349 | +- **Grid:** A layout system based on evenly-spaced rows and columns |
| 350 | +- **Iteration:** Repeating a process multiple times (like in a loop) |
| 351 | + |
| 352 | +## Going Further |
| 353 | + |
| 354 | +Computational poetry is a rich field with many approaches. Research these artists and techniques: |
| 355 | + |
| 356 | +- **Concrete poetry:** Poetry where the visual arrangement is as important as the words |
| 357 | +- **Blackout poetry:** Creating new poems by selectively revealing words from existing text |
| 358 | +- **Erasure poetry:** Similar to blackout poetry, removing words to create new meaning |
| 359 | +- **Artists to explore:** Allison Parrish, Nick Montfort, Lillian-Yvonne Bertram |
| 360 | +- **Online computational poetry:** [Taper](https://taper.badquar.to/), [The HTML Review](https://thehtml.review), [Random Walk](https://randomwalk.club/) |
| 361 | + |
| 362 | +## Resources |
| 363 | + |
| 364 | +- [L5 Documentation](https://l5lua.org/reference) |
| 365 | +- [More L5 Tutorials](https://l5lua.org/tutorials) |
| 366 | +- [L5 Examples](https://l5lua.org/examples) |
| 367 | + |
| 368 | +## Share Your Work! |
| 369 | + |
| 370 | +Created something interesting? Share it with the [L5 category](https://discourse.processing.org/c/l5/29) on the Processing Discourse forum or with #L5 on social media. We'd love to see what you make. |
| 371 | + |
| 372 | +--- |
| 373 | + |
| 374 | +*This tutorial was created for CCFest and is available under a Creative Commons Attribution 4.0 International License.* |
0 commit comments