dupdupdraw

Forthish drawing bot

Usage no npm install needed!

<script type="module">
  import dupdupdraw from 'https://cdn.skypack.dev/dupdupdraw';
</script>

README

This is a simple HTML canvas experiment with a forth-like language.

As a quick primer:

How does Forth work?

Forth is a stack-machine based postfix language. Words in your program are executed in order, and the stack starts empty. A stack is like a list where the first thing you put in is the first thing you pull out.

stack: []
program: 2 3 + 4

In many programming languages you might think this adds 3 and 4 and gives an error about 2, but the language for dupdupdraw doesn't work that way. Words are executed from left to right, so...

  1. 2 is a number, so it goes on the stack: [2]
  2. 3 is the same: [2 3]
    • is an instruction, so it adds the top two numbers and puts the result on the stack: [5]
  3. 4 is just like 2 and 3, so our final stack is [5 4].

Trying to use an empty stack or using a word you haven't defined causes an error in a real Forth, but in dupdupdraw it's handled this way:

  • if the stack is empty you get enough zeros to perform the operation (so "2 +" -> [2]).
  • unknown words are assigned a random value which is constant for the whole picture. So "asdf asdf /" -> [1], and a whole program of "asdf asdf asdf" would make a picture that was some shade of gray. Note random values are not consistent between runs of the generator.

How does dupdupdraw work?

Your program (like a tweet) is executed once for every pixel of a 512 by 512 image. The three values at the top (right) end of the stack are the r, g, and b values for that pixel, on a scale of 0 to 255. So [0 0 0] is black, [128 128 128] is grey, and [255 0 0] is red. Values outside the range are simply treated as 255 or 0 appropriately. Leftover values on the stack are ignored.

Some built in keywords change what they do depending on your pixel's location; the most obvious are x and y, which evaluate to the value for that pixel's location.

What are the built in words?

A word in parens is an alias. ? and @ are not keywords, but are often used as random values.

  • +-/* - basic math
  • = - equality test - if the top two values are equal, then push 1 onto the stack, otherwise push 0
  • mod (%) - modulus "7 4 %" -> [3]. A
  • // - As division, but take the floor (round down).
  • sqrt (sr) - square root
  • sin - sin of the value multiplied by 255
  • r - random and re-evaluated for every pixel (adds noise)
  • dist (di) - the distance of the current pixel from the location denoted by the two values on the top of the stack ("10 20 di" gives distance from x: 10, y: 20).
  • < > - "10 7 <" -> [0], "7 10 <" -> [1]. There's no if in dupdupdraw, but this can be used in much the same way ("x dup 256 < *" is zero where x is not less than 256, for example.)
  • xl / xg / yl / yg - If the top of the stack is less than x (for xl), leave the next value alone, otherwise replace with zero. xg -> x greater, yg -> y greater etc. This is good for sectioning your canvas.
  • dup - duplicate the top of the stack ("23 dup" -> [23 23])
  • swap - swap the top of the stack with the next value "1 2 swap" -> [2 1]
  • ish - inverse sinh, 64 / sinh(input / 256). Good for curves. Thanks to @itszutak for introducing me to sinh. (sinh is also available but it's a very steep curve)
  • max - remove the two values from the top of the stack and put the higher one back.
  • rot - put the value two down the stack on top. "1 2 3 rot" -> [2 3 1]
  • over - make a copy of the second value on the stack and push it. "1 2 over" -> [1 2 1]

If you have any questions, feel free to ask me on Twitter. Thanks!

-POLM