3. Infinite loops




Figure 3.1: Early alchemical illustration of ouroboros accompanied by the text ἓν τὸ πᾶν (“The All is One”) from the work of Cleopatra the Alchemist (10th Century). Image from Wikipedia

Loops offer alternative imaginaries, as is the case of the ancient image of a serpent eating its own tail. Ouroboros, from the Greek, expresses the endless cycle of birth and death, and therefore stands for the ability of processes to infinitely renew themselves. Alongside evocative references to autocannibalism and alchemy, loops are related to control and automation tasks, as well as repetitive procedures in everyday situations such as those heard in repeating sections of sound material in music.1 In programming, a loop allows the repeated execution of a fragment of source code that continues until a given condition is met, such as true or false. Indeed a loop becomes an infinite (or endless) if a condition never becomes false.

It was mathematician and author Augusta Ada Byron Lovelace who was one of the first to introduce and illustrate the concept of a programmatic loop in the early nineteenth century. She recognized that there were repeatable operations in the conceptual design of the first ever, automatic, general-purpose computing machine, Charles Babbage’s Analytical Engine. A loop, which she called a “cycle,” appears in her “Note G” on the Analytical Engine2 that describes the so-called Bernoulli numbers program, as in the diagram below. It utilizes two loops to indicate the repetition of a set of instructions with conditions,3 thereby minimizing efforts to write a repeatable operation in duplicate. As such, loops address repeatable and operational time.

ada lovelace

Figure 3.2: Diagram for the computation by the Engine of the Numbers of Bernoulli, from “Note G” by Ada Lovelace (1843). Image from Wikimedia Commons

Loops in contemporary programming are highly influenced by these early insights into the handling of repeated machine operations expressed in diagramatic form. High-level programming languages such as p5.js include this loop concept, allowing a fragment of source code to be repeatedly executed, as in the example of the draw() function that will run continuously until the program is stopped or using the syntax noLoop(). Loops are some of the most basic and powerful of programming concepts.

The main example for this chapter is the graphical spinning wheel icon of a preloader, or so-called “throbber,”4 that indicates when a program is performing an action such as downloading or intensive calculations. We consider this an evocative symbol as it illuminates the discrepancy between what we think we know and what we don’t know about the hidden machine labor, and the complexity of multiple temporalities that run during any given computational operation.5 It is a good icon to illustrate how loops work, allowing us to contemplate the entanglement of perceptible streams and computational logics, as well as how we experience the historical present through digital media.6 As we shift from static objects to moving ones, the animated throbber will guide the programming tasks related to thinking about transformation (such as rotation and translation), but will also act as a suitable analytical object for us to think through the idea of loops, the related temporal elements, and time-related syntaxes more conceptually.


We usually encounter the ubiquitous spinning icon while loading or streaming. It shows that a certain operation is in progress, but what exactly is happening, and how long this will take, is not at all clear. There is no indication of progress or status as is the case with a progress bar, for instance. We see the icon spinning, but it explains little about what goes on in the background or about timespan. Learning to program a throbber, and, subsequently examining Asterisk Painting by John P. Bell — that creates a series of asterisks by repeatedly printing the number of milliseconds that have passed since the painting started — will help you gain insight into the way programming employs transformational movement and loop structures, and, at the same time, an insight into the temporal operations of computational processes.

Exercise in class (Decode)

As mentioned above, this chapter shifts from programming static objects to a mixture of both static and moving objects. Our example is circular and spins, as if it were eating its own tail.


Figure 3.3: The runme of sample code - sketch 3_1

RunMe, https://aesthetic-programming.gitlab.io/book/p5_SampleCode/ch3_InfiniteLoops/sketch3_1/

Can you describe the various elements and how they operate computationally in your own words?

1. Speculation

Based on what you see/experience on the screen, describe:

Further questions:

2. Experimentation

3. Mapping

4. Technical questions/issues

5. Other conceptual questions

Source code

function setup() {
 //create a drawing canvas
 createCanvas(windowWidth, windowHeight);
 frameRate (8);  //try to change this parameter

function draw() {
  background(70, 80);  //check this syntax with alpha value

function drawElements() {
  let num = 9;
  //move things to the center
  translate(width/2, height/2);
  /* 360/num >> degree of each ellipse's movement;
  frameCount%num >> get the remainder that to know which one
  among 8 possible positions.*/
  let cir = 360/num*(frameCount%num);
  fill(255, 255, 0);
  //the x parameter is the ellipse's distance from the center
  ellipse(35, 0, 22, 22);
  stroke(255, 255, 0, 18);
  //static lines
  line(60, 0, 60, height);
  line(width-60, 0, width-60, height);
  line(0, 60, width, 60);
  line(0, height-60, width, height-60);

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);


A function in p5.js starts with the syntax function() {}, containing “a self-contained section of code”8 to peform a certain task. The most basic built-in functions in p5.js are setup() and draw() that specify the contained code in relation to a particular purpose such as setting up the environment in which to run the program, as well as doing things over time. Other built-in functions in the sample code provided, such as windowResized(), serve to read just the canvas size if there is any window resizing event. The canvas size is not set at fixed dimensions, but is subject to the window size that you have adjusted as illustrated in the code. This was also discussed in the preceding chapter: createCanvas(windowWidth, windowHeight);. The function windowResized() suggests that an “event listener” — a procedure or function in a computer program that monitors for an event to occur — initiates at the code level to not only run once, but “constantly.” It is “listening” to events of window resizing specifically, and similar to other listening events such as mouseIsPressed() that was introduced in the previous chapter. The windowResized() function is considered asynchronous, which means some other events occur concurrently with the main flow of the program such as, for instance, drawing shapes.

Alongside built-in functions, the sample code contains the custom function function drawElements(); which is invoked in Line 13: drawElements(); within the draw() function. Defining a function is relatively simple in JavaScript. Type the keyword “function” then follow it with the name that you want to give your function. The function name “drawElements” gives you a sense of what this function does, which is draw ellipses, lines of a particular size, position, and color, as well as drawing ellipses and lines to rotate clockwise or statically remain in place. There are many ways of drawing the same result, but as we are still in the early stages of learning to program, we will therefore work on an example that can do the same, but aligns better with our learning progress. With this in mind, some of the code is intentionally written in a way that is less efficient, but serves the purpose of illuminating key concepts.

Programmers like to split large tasks into smaller operations and procedures, so they are easier to structure, manage, debug, read, and are easier to collaborate on with multiple programmers. In function drawElements();, the sample code is simply separated from the function draw(), clearly indicating that this particular part of the code relates to drawing the various on-screen elements. Of course you could also separate the drawing of ellipses and lines, and it is a subjective and situated decision to decide how best to separate the different tasks.

There is another type of function where you can specify tasks with arguments that are passed to the function and receive a return value. See the example below:

let x = sum(4, 3, 2);   
//passing values four as a, three as b, two as c to the function sum
function sum(a, b, c) {
  return a + b + c; //return statement


Exercise in class

You can also try to type/copy the above code into your own sketch, where it will return the number 9 as the output in the web console because this is the result of the sum of the values 4, 3 and 2. These values are called “arguments” that are passed to the function (i.e. sum()). In the example, the parameters as variables a, b and c equals to the actual values 4, 3 and 2 as arguments, but the value of the variables can be changed. The function “sum” can be reused if you pass on other arguments/values to it, as for instance another line of code let y = sum(5,6,7); and the return value of y would be 18. You can try to come up with your own functions and arguments.


In general, the transform-related functions9 apply a two-dimensional or three-dimensional transformation to an element or object. In the sample code provided with the throbber, two specific transformational functions were used to move the canvas and create an illusion of object transformation. (It is important to know that the transformation is done at canvas background level, not at the individual shape/object level.)


Figure 3.4: Moving the coordinate system at canvas level. Image from processing.org

  1. translate(): This function displaces/moves objects within the display window. For example, moving the canvas to the center will position the whole sketch at the center too (translate(width/2, height/2);). The ellipse is drawn as ellipse(35, 0, 22, 22) which takes (35, 0) as the x and y coordinates, and “22” as the size. If we don’t have the translate() function upfront, the ellipse will be placed at the top left corner instead (because the x coordinate value “35” is the distance of the rotating ellipses from the center position). By moving the coordinate origin to the middle using the translate() function, the ellipses is placed in the middle of the canvas, because the coordinate orign (0,0) has moved to the center of the screen. Building upon the previous chapter on the spatial dimension of a coordinate system, “translate” adds another layer to think about moving and positioning objects using canvas.
  2. rotate(): In this sample code with the throbber, the use of the function rotate() makes the ellipse rotate through a particular number of degrees. The default unit for rotation is radians. As such, the code is written as rotate(radians(cir));. The function rotate() takes radians in its default mode, but if you want to change to degrees all you have to do is add the code angleMode(DEGREES).

In order to continue expanding on spatial relationships, the entanglement of time and space is made apparent in this example by using the rotate() function that operates alongside other time-related syntax in draw(). There are a total of 9 ellipses (indicated as let num=9;), and each is separated from the next by 40 degrees (i.e 0.968 rad) which is derived from “360/9.” A circle has 360 degrees and to rotate the ellipse over time, it requires the time element to calculate when, how, and where to move. This is how the function frameCount() works as it counts the number of frames displayed since the program started.10 The Line 21 let cir = 360/num*(frameCount%num); illustrates the use of a “modulo” operation to find the remainder or the number that’s left after it is divided by another value. As such, the value of the variable cir is limited to multiples of 40: “0, 40, 80, 120, 160, 240, 280 and 320.” On the basis of the cir value, the program follows such a sequence over time to rotate one after the other, based on the original position, then repeats continuously.

push() and pop()

Functions of push() and pop() are commonly used to save the current style and restore settings respectively. Style as in color and setting as in rotate and translate. In the sample code, rotation is only applied to the centered ellipses when four lines at each side are fixed. The following excerpt of code will help to explain:

function drawElements() {
  let num = 9;
  //move things to the center
  stroke(255, 255, 0,18);
  //static lines
  line(60, 0, 60, height);
  line(width-60, 0, width-60, height);
  line(0, 60, width, 60);
  line(0, height-60, width, height-60);

The last four lines describe the drawing of the four static yellow lines. Logically speaking, the translate and rotate functions should also apply to these lines, but because the pop() function is placed right after drawing all the ellipses it does not impact the lines (see Figure 3.5). But if you move the line pop() to the end, then the lines will also rotate and translate (see Figure 3.6). This illustrates how push() and pop() can be used to save and restore styles, and how their placement matters.11


Figure 3.5: Different placement of the pop() function - Four static yellow lines


Figure 3.6: Different placement of the pop() function - Four rotating yellow lines

Exercises in class

  1. Change the arguments/values, as well as the position/sequence of the sample code to understand the functions and syntax such as the variable num, the transformational functions translate() and rotate(), as well as saving and restoring current style and transformations such as push() and pop().

  2. We have explained how to use rotate() to display the ellipses at various degrees of rotation, but how about the fading in and out of each ellipse in the sketch? (Hint: as this is repeatedly faded in and out, the background() syntax under the function draw() is key to producing such effects.)

  3. This exercise is about structuring code. How would you restructure the sample code so that it is easier for others to understand, but still maintains the same visual outcome? There are no right or wrong answers, but some pointers below might facilitate discussion:

  4. You may rename the function and/or add new functions
  5. Instead of having drawElements(), you might have something like drawThrobber() and drawLines()?

Asterisk Painting

The following section will move from repetition and regularity, to repetition and difference. Artist and software developer John P. Bell made an artwork called Asterisk Painting,12 that consists of a number of throbber-like spinning patterns, however each throbber (or what he calls “asterisk”) spins differently, varying in color and texture. Many of the syntaxes Bell used are related to temporality, for example the setting up of a timer, the calculation in milliseconds, the speed of rotation, the time to wait before starting a new cycle, and so on, in which programming enables the re-negotiation of time to “develop alternative time practices and experiences” through manipulating time-related functions.13 Also, on a closer inspection, the asterisks are not geometric shapes, but are constituted by a series of numbers which refer to the milliseconds counter that line up to form a straight line.


Figure 3.7 : Asterisk Painting (2014) by John P. Bell. Courtesy of the artist

According to Bell,

Asterisk Painting is programmed to create a series of asterisks by repeatedly printing the number of milliseconds that have passed since the painting started. If left to run by itself it will do so; however, when started on a real system, delays external to my artwork may make the asterisks look more like spots […]”

Source code

The original piece was written in Processing and has been modified, and ported to p5.js. It is a much more complex program than the first one, but we still wanted to include this as an addition to this chapter as it helps to demonstrate the potential for further developing a looping sketch and reflect more deeply on infinite loops, and the use of time-related syntaxes.

Asterisk Painting by John P.Bell (http://www.johnpbell.com/asterisk-painting/)
Original code in Processing: https://tinyurl.com/AsteriskPainting
Port to p5js and modified by Winnie Soon with comment notes,
last update: 25 Aug 2020

changes log:
1. The color mode has been changed to a variable
as the push/pop function will restore the previous fill color state.
2. Remove font
3. change the bg color
4. add text size
5. remove load signature image
6. change the canvas size and corresponding no. of asterisk
7. display a counter on the bottom left corner and in black color
8. Add extensive comments
9. return a random no in integer

let xDim = 1000;  //canvas size-width
let yDim = 600;   //canvas size-height
let timer = 0;
let speed = 100;  //the speed of rotating , default 100
let maxSentences = 77;  //original: 77
let sentences = 0;
let xPos = [1, 2, 3, 4, 5]; //original: 8 columns
let yPos = [1, 2, 3, 4]; //original: 5 rows
let xCtr = 0;
let yCtr = 0;
let waitTime = 10000;
let itr = 0; // no. of iteration
let milliStart = 0;
let currentMillis;
let fillColor;

function setup(){
  createCanvas(xDim, yDim);
  /*calculate the x-position of each asterisk as
  an array (xPos[]) that starts with an array index[0]*/
  for(let i = 0; i < xPos.length; i++) {
    xPos[i] = xPos[i] * (xDim / (xPos.length+1));
  /*calculate the y-position of each asterisk as
  an array (ypos[]) that starts with an array index[0]*/
  for(let i = 0; i < yPos.length; i++) {
    yPos[i] = yPos[i] * (yDim / (yPos.length+1));
  fill(0);  //counter color at the bottom left
  textAlign(LEFT, CENTER);
  text(itr, 10, yDim-30); //display counter
  fillColor = color(
    floor(random(0, 255)),floor(random(0, 255)),floor(random(0, 255))

function draw(){
  //millis means millsecond since starting the program, like frameCount
  currentMillis = floor(millis() - milliStart);
  if(currentMillis > timer){
    translate(xPos[xCtr], yPos[yCtr]);  //rows and cols
    rotate(radians((360/8)* (millis()/speed)));  //rotate in itself
    timer = currentMillis + speed; //the time for the next loop
    /* about the time string written in the form of an asterisk,
    and it starts with 0 always.
    nf:format numbers into strings and adds zeros in front
    3 digits in front and 0 digit after the decimal. */
    text(nf(currentMillis, 6), 3, 0);
    if(sentences >= maxSentences){  //reach the max for each asterisk
      xCtr++;  //move to next array
      //meet max cols, and need to go to next row
      if(xCtr >= xPos.length) {
        xCtr = 0;
        yCtr++;  //next row
        /* the program reaches the max no. of rows on a screen
        (i.e after reaching the no. of max cols); the screen is
        filled > reset everything and update the counter*/
        if(yCtr >= yPos.length){
          yCtr = 0;
          //add counter (iteration)
          //counter's display color
          //change the counter display again
          text(itr, 10, yDim-30);
          //wait for next round for starting the first asterisk
          let wait = floor(millis() + waitTime);
          while(millis() < wait){}
          //reset the starting time
          milliStart = millis();
          //reset the timer
          timer = 0;
      sentences = 0;
      fillColor = color(
    pop();  //restore previous state

Exercise in class

RunMe, https://aesthetic-programming.gitlab.io/book/p5_SampleCode/ch3_InfiniteLoops/

1.Read the source code above. A reminder of the code comments:

2.Use the decoding method that we introduced previously in this chapter, try to speculate, experiment, and map your thoughts to the source code.


To be able to get a deeper understanding of the source code, you only need a few more fundamental concepts of programming. The first one is “Array,” which is commonly understood as a list of data and is related to previous concepts such as variable and data types. If we need to work with a chunk of data, such as a collection of words, you can use arrays instead of making separate variables. For example:

let words = [] //array -> start with 0
words[0] = "what";
words[1] = "are";
words[2] = "arrays";
console.log(words[2]); //output: arrays
console.log(words.length); //output: 3

We can follow a similar structure to our previous approach using variables:

  1. Declare: Think of a name you want to use to store the list of values. The symbol [] in let words = [] indicates “words” is structured as an array, but how many words is unknown and hasn’t been specified with just this line of code.
  2. Initialize/Assign: Given the example above, there are three text values to store in quotations (this indicates they are “String” data types): “what,” “are,” and “arrays.” Since an array is a list of values and it is needed to be identified individually, “an array index” within a square bracket is used to represent the position of each piece of data in an array. It starts with [0] as the first item, then [1] as the second, and so forth. Therefore words[0] ="what" means that the first index item of the array words is a string data type and with the value “what.”
  3. Re(use): The console.log() function is an example that indicates how you can retrieve and use the data, how you can print it in the web console area, or how you can draw on a canvas.

The syntax arrayname.length is used to ask how many items there are in an array.

Let’s look at the sample below from Asterisk Painting:

let xPos = [1, 2, 3, 4, 5];
let yPos = [1, 2, 3, 4];

This is a slightly different way of declaring an array. It combines both the declaration and initialization/assignment into a single line to both declare the array names as xPos and yPos with the term let, and then assigns the numeric values into the array index, which refers to the number of columns and rows respectively. Think about it like this: the program needs to know how many asterisks should be drawn on the screen before moving on to the next row as well as when to restart (the asterisks fill the entire canvas in terms of reaching the maximum number of rows and columns.)

As the array index starts with [0], therefore each index has mapped the value in this way:

let xPos = [1,2,3,4,5];
→ The xPos.length is 5 and that indicates 5 values are being stored in this array: xPos[0] = 1, xPos[1] = 2, xPos[2] = 3, xPos[3] = 4, xPos[4] = 5.
let yPos = [1,2,3,4];
→ The yPos.length is 4 and that indicates 4 values are being stored in this array: ypos[0] = 1, yPos[1] = 2, yPos[2] = 3, yPos[3] = 4.

The above two arrays store each asterisk’s center position in the form of x and y coordinates.

There are also methods of adding or removing an array index:

→ To add a value to the end of the array. Example: xPos.push(6) will extend the index to xPos[5] = 6.
→ This will remove a range from an array index, or remove the existing index, and replace it with new indexes with other values.

Conditional statements

The discussion of conditional statements in the previous chapter will make it easier to follow Asterisk Painting‘s code. We follow the conditional structure (if-then) built into the program in order to know when to move from one asterisk to the next.

if(sentences >= maxSentences){  //reach the max for each asterisk
  //move to the next one and continues;

The value of the variable maxSentences is 77 (refer to Line 5 from the source code), therefore each asterisk contains 77 sentences (in the form of a line that contains numbers). The other variable sentences counts each line and the program checks whether the current sentences count has reached its maximum. “If” the asterisk reaches 77 sentences “then” it will move to the next one and the sentences counter will be reset to zero (Line 84) and start counting again. The logic repeats across all the asterisks within the draw() function.


The core concept of a loop is that it enables you to execute a block of code many times. For example, if you have to draw one hundred lines that are placed vertically one after the other, you can of course write one hundred lines of code using the syntax: line().

A “for-loop” allows code to be executed repeatedly, and so provides an efficient way to draw the line one hundred times by setting up a conditional structure, counting the number of lines that have been drawn and counting the maximum number of lines. Similarly, in this sketch, there are some elements needed to run repeatedly, but there is an end, such as calculating the center point using the exact x and y coordinates for each asterisk which are based on the width and height of the canvas. Knowing how many columns and rows make up a canvas allows us to know the values of the center point for drawing each asterisk.

To structure a for-loop, you need to ask yourself:

The following is an excerpt from Asterisk Painting (Lines 20-29):

/*calculate the x-position of each asterisk as
an array (xPos[]) that starts with an array index[0]*/
for(let i = 0; i < xPos.length; i++) {
  xPos[i] = xPos[i] * (xDim / (xPos.length+1));
/*calculate the y-position of each asterisk as
an array (ypos[]) that starts with an array index[0]*/
for(let i = 0; i < yPos.length; i++) {
  yPos[i] = yPos[i] * (yDim / (yPos.length+1));

See the structure of a for-loop:

Figure 3.8 A for-loop

Figure 3.8 shows you what a for-loop consists of:

  1. A variable declaration and initialization: Usually starts with 0
  2. A specificed condition: The criteria to meet the condition
  3. Action: What you want to happen when the condition is met
  4. Loop for next: For the next iteration (usually incremental/decremental).

This block of code from the above example describes the position of each asterisk in terms of its x and y coordinates (the center point [x, y] of each asterisk). Since there are 5 columns (xPos) and 4 rows (yPos) which have been defined in global variables, the program needs to know the coordinates precisely. The overall formula to locate the position, for example xPos, is to divide the width of the canvas by the number of asterisks horizontally, and add one (see Figure 3.9). As such, the code can be understood as follows: calculate the xPos[i] for each iteration with the starting point 0. Additionally, each iteration will increase the count by 1 until it reaches the maximum number of asterisks in a row (i < xPos.length).

Figure 3.9 The xPos of each asterisk

In our teaching, we describe another example of the use of a for-loop to further clarify its use and to demonstrate the repeated drawing of objects. This example (see Figure 3.10) draws 20 ellipses and each with a distance of 20 pixels.

Figure 3.10 Drawing 20 ellipses on a canvas using a for-loop

let x = 20;

function setup() {
  createCanvas(420, 100);
  for (let i = 0; i < 20; i++) {
    ellipse(x, 45, 15, 15);
    x += 20;

In this simple ellipse drawing, the key is the local variable i (see Linr 6 above, which is used to set the start of the counting of the ellipses: let i = 0;, as well as setting the condition of how many ellipses should be drawn: i < 20, and counting the ellipses for each iteration: i++). The global variable x is used to determine the position (in terms of x axis or what could be described as the distance) of each ellipse and to make sure the program will increment 20 pixels for each iteration: x+=20. In this way we use a for-loop to draw multiple ellipses, instead of having 20 lines with fixed x and y coordinates.

The “while loop” is another type of loop for executing iterations. The statement is executed until the condition is true and stops as soon as it is false.

For example, while(millis() < wait){} in Line 76 of the work Asterisk Painting tells the computer to do nothing if the value of millis()16 is less than the value of the wait variable. millis() is a time-related syntax and returns the number of milliseconds since the program started which makes it similar to frameCount(). Once the condition is false (i.e. millis() is no longer less than wait), the loop will end, and the program can proceed to the next line. This example is located towards the end of the program when all the asterisks have been drawn, but the program needs to wait a certain amount of time before resetting (clearing) the canvas and starting again. This while-loop therefore serves as a pause, freezing the program from running because there is literally nothing between the opening and closing brackets.


As we have established, loops execute a block of code as long as a specified condition is true. In this closing section to the chapter it seems appropriate to emphasize that while-loops and for-loops allow code to be executed repeatedly based on a given condition. The loop can be thought of as a repeating “if” statement and offers a good way of challenging conventional structures of linear time, and demonstrating how computers utilize time differently. Programming challenges many of our preconceptions about time including how it is organized, how the present is rendered using various time-specific parameters and conditions, as in the case of a throbber. We hope it is already clear that machine-time operates at a different register from human-time, further complicated by global network infrastructures, and notions of real-time computation.

What it means to begin and end a given process becomes a philosophical problem. In “The Computer as Time-Critical Medium,”17 Wolfgang Ernst clarifies the ontological importance of time to the computer to operate and perform tasks. He points to key issues of programmability, feedback, and recursion at programming-language level in ways that we hope resonate with the examples we have provided in this chapter.18 Precise technical detail is crucially important for the discussion, and his example is how “time counts” differently in the computer, as for example with the clock signal. Ernst’s concept of “micro-temporality” is useful as it draws attention to the issue of temporality in programming in ways that many of the discussions of software overlook, and furthermore how — in a philosophy of time — the technical or mathematical layer is often dismissed as deterministic.19

Loops offer alternative imaginaries of time. In his conference paper “… Else Loop Forever,” Ernst develops this discussion in relation to “untimeliness.”20 He draws on the infamous “halting problem” that underpins Turing computation and refers to the problem of whether a computer program, given all possible inputs, will finish running or continue to run forever. In his 1936/37 essay “On Computable Numbers, with an Application to the Entscheidundsproblem,” it was Turing’s assertion that a general algorithm to solve the halting problem was not possible which led to the mathematical definition of a Turing machine.21 This “problem of decision,” or “ending” as Ernst puts it, underscores broader notions of algorithmic time and the way the computer forever anticipates its own “never-ending” in an endless loop. Perhaps the throbber icon is a good metaphor for this, in terms of the impossibility of predicting the quality of transmission conditions, and, in this way, the animated graphics depict a sense of uncertainty that underlies deep processual micro-temporality.22

Contrary to any traditional narrative — with its beginning, middle and end – Ernst points out that a computational recording can be re-enacted endlessly: “with no internal sense of ending,” as a “time-critical condition.”23 That there can be “no happy ending” allows Ernst to elaborate on new temporal structures that are no longer aligned to traditional narrative structures or the terminal logic of the “end of history.”24 Our first example of the throbber alludes to this blurring of the beginning and the end. Temporal complexity is further developed by referring back to Turing’s speculation on artificial intelligence, whether a finite-state machine can be aware of its “conscious” state at a given time and whether a sense of ending is necessary in order to be functional. It is clear that finite-state machines are procedural, in the sense that they operate linear sequences of discrete events in time like clockwork, but as Ernst reminds us: “There is no automatic procedure which can decide for any program, if it contains an endless loop or not.”25

Referencing Martin Heidegger’s “being-in-time,”26 and human beings’ knowledge of the end of their lives which inscribes a temporal sense of what it means to be a human, Ernst says: “Humans live with the implicit awareness that their death is already future in the past.”27 This looped deferral of ending is ontologically exacerbated with computation, unfolding the ending of being as a time-critical condition for both humans and machines alike. Leaving aside a deeper discussion of Heidegger’s philosophy, the importance of this for the discussion of loops seems to mirror the complexity of lived time. Programming manages to provide insight here, and creative opportunities as is the case with live coding during which programmers interact with a running system that is not stopped while waiting for new program statements.28 We can even begin to speculate on how software is not only synchronized with lived time, but actually produces it, and we hope the two examples in the chapter help us to think through the intersection of endlessness, loops, conditions, and temporalities in both conceptual and technical ways. We might go as far as to say that programming allows for a time-critical understanding of how technologies play a crucial role in our experience of time, not only how we model it, but how we can forge new beginnings and endings.

MiniX: Designing a throbber


For additional inspiration:

Check out other works that refer to the throbber and how other people contextualize their thinking.

Task (RunMe):

Use loops and any one of the transformational functions to redesign and program an “animated” throbber.

Questions to think about (ReadMe):

Describe your throbber design, both conceptually and technically.

Required reading

Further reading


  1. The logic behind loops can be demonstrated by the following paradoxical word play: “The next sentence is true. The previous is false.” Further examples of paradox, recursion, and strange loops can be found in Douglas R. Hofstadter’s‘ Gödel, Escher, Bach: An Eternal Golden Braid (New York: Basic Books, 1999). 

  2. For an account of “Note G,” see Joasia Krysa’s Ada Lovelace 100 Notes-100 Thoughts Documenta 13 (Berlin: Hatje Cantz Verlag, 2011). 

  3. Eugene Eric Kim and Betty Alexandra Toole, “Ada and the First Computer,” Scientific American 280, no. 5 (1999), 78. 

  4. It is also interesting to note that the term “throbber” is a derogatory term derived from erect penis, not unlike git which was described in the opening chapter. 

  5. There is much we could add here also about screensavers as cultural form in the broader context of productive labor-time, and the attention economy. Alexandra Anikina’s PhD Procedural Films (Goldsmiths, University of London, 2020) contains a chapter on the aesthetic form of screensavers in relation to the discussion of idle time/sleep, and cognitive labor; her lecture-performance Chronic Film from 2017 can be seen at http://en.mieff.com/2017/alexandra_anikina. See also Rafaël Rozendaal’s installation Sleep Mode: The Art of the Screensaver at Het Nieuwe Instituut (2017), https://hetnieuweinstituut.nl/en/press-releases/sleep-mode-art-screensaver

  6. Winnie Soon, “Throbber: Executing Micro-temporal Streams,” Computational Culture 7 (October 21, 2019), http://computationalculture.net/throbber-executing-micro-temporal-streams/

  7. Artist Golan Levin has given an online tutorial on modulo operator as part of The Coding Train series, see: https://www.youtube.com/watch?v=r5Iy3v1co0A

  8. Derek Robinson, “Function,” in Matthew Fuller, ed. Software Studies, 101. 

  9. To stick with the provided examples, we only offer two syntaxes related to transformation. Beyond translate() and rotate(), there are also other transform-related functions such as scale(), shearX(), shearY(). See https://p5js.org/reference/#group-Transform

  10. https://p5js.org/reference/#/p5/frameCount

  11. https://p5js.org/reference/#/p5/push

  12. http://www.johnpbell.com/asterisk-painting/.>. 

  13. Hans Lammerant, “How humans and machines negotiate experience of time,” in The Techno-Galactic Guide to Software Observation (Brussels: Constant, 2018), 88-98. 

  14. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push 

  15. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice 

  16. millis() is a p5.js syntax, returning the number of milliseconds since starting the program, similar to frameCount but counted in milliseconds, see https://p5js.org/reference/#/p5/millis

  17. Wolfgang Ernst, Chronopoetics: The Temporal Being and Operativity of Technological Media (London: Rowman & Littlefield International, 2016), 63-95. 

  18. Ernst, Chronopoetics, 63. 

  19. For example, the philosopher Henri Bergson makes a qualitative distinction between lived “durational” time, and vulgar, or clock time, which flattens and deadens the experience of time. See Henri Bergson, Matter and Memory [1896] (New York: Zone Books, 1990). 

  20. Wolfgang Ernst, “‘… Else Loop Forever’. The Untimeliness of Media” (2009). Available at https://www.medienwissenschaft.hu-berlin.de/de/medienwissenschaft/medientheorien/downloads/publikationen/ernst-else-loop-forever.pdf

  21. Alan M. Turing, “On Computable Numbers, with an Application to the Entscheidungs problem,” Proceedings of the London Mathematical Society 42 (1936/1937): 230–265. 

  22. Soon, “Throbber.” 

  23. Ernst, “‘… Else Loop Forever’.” 

  24. The end of history” is a reference to Francis Fukuyama’s The End of History and the Last Man (New York: Free Press, 1992), which proposes the ascendancy of Western liberal democracy after the dissolution of the Soviet Union, post-1989. 

  25. Ernst, “‘… Else Loop Forever’.” 

  26. Martin Heidegger, Being in Time (1927). For a useful summary, see https://plato.stanford.edu/entries/heidegger/#BeiTim. 

  27. Ernst, “‘… Else Loop Forever’.” 

  28. See forthcoming Alan Blackwell, Emma Cocker, Geoff Cox, Thor Magnussen, Alex McLean, Live Coding: A User’s Manual (publisher and date unknown).