Begin your 2023 with Genuary!

by @EthanThatOneKid

December 27, 2022 • 14 min read

ACM at CSUF welcomes Genurary 2023!

Welcome to your introductory Genuary 2023 codelab where you will learn how to use https://editor.p5js.org/ to generate artwork for Genuary 2023.

What is Genuary? ❄️

Genuary 2023's official website https://genuary.art/ defines Genuary as:

Genuary is an artificially generated month of time where we build code that makes beautiful things.

There is a unique prompt for each day in the month of January. All of these prompts encourage the practice of creative expression in programming.

In our club Discord server, members are encouraged to share their creations throughout the monthlong event: Genuary 2023.

What is this codelab about?

In this codelab, you will learn how to create your first generative artwork with https://editor.p5js.org/ for Genuary 2023.

Once you have completed this codelab, you will have created a generative artwork using the 10print algorithm.

Quickly, what is 10print?

10print is a generative algorithm that creates a pattern of lines.

Simulated output of the 10PRINT one-liner BASIC program for the Commodore 64

source: Wikipedia

Our algorithm will draw out the pattern of lines on a canvas from the top left corner to the bottom right corner.

The algorithm is based on the following rules:

  • 50% of the time, draw a line from the top left corner to the bottom right corner.
  • The other 50% of the time, draw a line from the bottom left corner to the top right corner.

Supplemental reading:

Set up https://editor.p5js.org/ for Genuary

Sign in

Sign in to https://editor.p5js.org/ with your GitHub account.

Create a new sketch

To create a new sketch from scratch, go to https://editor.p5js.org/ and click on File > New.

Save the sketch with whatever name you like, or use a pattern such as genuary-2023-01-01.

In your sketch, you will see a setup() function and a draw() function.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
}

Start by removing background() from the draw() function.

Also, add a stroke() function to the setup() function to set the color of the lines to white.

function setup() {
  createCanvas(400, 400);
  stroke(255);
}

Make globals for the sketch

Above setup(), we will define global variables for the sketch.

let x = 0;
let y = 0;
let s = 10;

Draw a diagonal line

In the draw() function, draw a diagonal line with line().

Press to reveal the code
function draw() {
  line(x, y, x + s, y + s);
}

Run ▶️ the sketch to see that the line is drawn from the top left corner to the bottom right corner of the first square, which is 10 pixels by 10 pixels.

Draw a second diagonal line

To draw a second diagonal line, we will need to change the value of x by s pixels in each frame to move the next diagonal line to the right.

Press to reveal the code
function draw() {
  line(x, y, x + s, y + s);
  x += s;
}

Run ▶️ the sketch to see that an entire row of diagonal lines is drawn.

Draw the second row of diagonal lines

To draw the second row of diagonal lines, we will need to change the value of y by s pixels each time x reaches the end of the canvas to move the next diagonal line down. Do not forget to send x back to 0 when x reaches the end of the canvas.

Press to reveal the code
function draw() {
  line(x, y, x + s, y + s);
  x += s;
  if (x >= width) {
    x = 0;
    y += s;
  }
}

Run ▶️ the sketch to see that the entire canvas is filled with diagonal lines, but they are all drawn from the top left corner to the bottom right corner.

How many frames does it take to draw the entire canvas?

Since we know the width and height of the canvas, we can calculate the number of frames it takes to fill the canvas with one diagonal line per frame.

To do this, we will need to know the number of squares that fit horizontally and vertically on the canvas.

  • width / s = number of squares that fit horizontally
  • height / s = number of squares that fit vertically

We can then multiply the number of squares that fit horizontally by the number of squares that fit vertically to get the total number of squares.

(width / s) * (height / s) = total number of squares
Press to reveal the answer

Assuming you are drawing one diagonal line per frame, we can then calculate the number of frames it takes to fill the canvas given that the canvas is 400 pixels by 400 pixels and each square is 10 pixels by 10 pixels.

(400 / 10) * (400 / 10) = 1600 frames

Keep in mind how to get the number of frames it takes to fill the canvas in order to create a GIF later.

Randomize the direction of the diagonal line

To randomize the direction of the diagonal line, we can use random() to generate a random number between 0 and 1.

We can tell our program to draw a line from the top left corner to the bottom right corner if the random number is greater than 0.5, and to draw a line from the bottom left corner to the top right corner of the random number is less than 0.5.

Press to reveal the code
function draw() {
  if (random() > 0.5) {
    line(x, y, x + s, y + s);
  } else {
    line(x + s, y, x, y + s);
  }

  x += s;
  if (x >= width) {
    x = 0;
    y += s;
  }
}

Run ▶️ the sketch to see that the direction of the diagonal line is randomized.

Draw infinitely

To start the animation over when it reaches the end of the canvas, we can set the y value back to 0 when y reaches the end of the canvas and clear() the canvas.

Press to reveal the code
function draw() {
  if (y >= height) {
    y = 0;
    clear();
  }

  if (random() > 0.5) {
    line(x, y, x + s, y + s);
  } else {
    line(x + s, y, x, y + s);
  }

  x += s;
  if (x >= width) {
    x = 0;
    y += s;
  }
}

Save the animation as a GIF

Use saveGif() to save the animation as a GIF.

Hint: Review saveGif() short on YouTube.

Press to reveal the code

Allocate a boolean variable filming to keep track of whether the animation is being recorded to GIF.

let x = 0;
let y = 0;
let s = 10;
let filming = false;

Set filming to false when the animation completes.

function draw() {
  if (y >= height) {
    filming = false;

    y = 0;
    clear();
  }

  if (random() > 0.5) {
    line(x, y, x + s, y + s);
  } else {
    line(x + s, y, x, y + s);
  }

  x += s;
  if (x >= width) {
    x = 0;
    y += s;
  }
}

Make your call to saveGif() in the mouseClicked() function. When the mouse is pressed, the animation will be restarted and recorded to GIF.

Feel free to change the name of the GIF, but make sure the number of frames is the same as the number of frames it takes to fill the canvas.

function mousePressed() {
  if (!filming) {
    saveGif(
      "genuary-2023-01-01.gif",
      (width / s) * (height / s),
      { units: "frames", delay: 0 },
    );

    x = 0;
    y = 0;
    clear();

    filming = true;
  }
}

Run ▶️ the sketch and then click the canvas with your mouse to see that the animation is saved as a GIF.

Congratulations!

You have created your first animation for Genuary 2023 which concludes this
codelab!

Press to reveal the completed codelab
let x = 0;
let y = 0;
let s = 10;
let filming = false;

function setup() {
  createCanvas(400, 400);
  stroke(255);
}

function draw() {
  if (y >= height) {
    filming = false;

    y = 0;
    clear();
  }

  if (random() > 0.5) {
    line(x, y, x + s, y + s);
  } else {
    line(x + s, y, x, y + s);
  }

  x += s;
  if (x >= width) {
    x = 0;
    y += s;
  }
}

function mousePressed() {
  if (!filming) {
    saveGif(
      "genuary-2023-01-01.gif",
      (width / s) * (height / s),
      { units: "frames", delay: 0 },
    );

    x = 0;
    y = 0;
    clear();

    filming = true;
  }
}

Next steps

Feel free to add more features to your animation, such as:

  • Randomize the color of the diagonal line.
  • Change the size of the diagonal line.
  • Customize the way in which the diagonal lines are drawn.

If you are interested in learning more about p5.js, check out the following references:

More on saveGif()

It is no coincidence that this codelab features the saveGif() API. The first prompt of Genuary 2023 is "Perfect loop / Infinite loop / endless GIFs". The saveGif() API provided by p5.js is a powerful tool that happens to help with Genuary 2023 day 1 in particular, and even with other prompts later this month at the artist's discretion.

Social media

You are encouraged to share your GIF on your favorite social media and tag it with #genuary and #genuary2023 and also #genuary1, #genuary2, etc, depending on which prompt you are working on.

Do not forget to send your GIF to the associated thread on our club Discord server, https://acmcsuf.com/discord!

Credits

Read as TXT: /blog/742.txt