Wednesday, 25 October 2017

Ideas for Making Processing Even Easier for First Time Coders

Update - looks like these suggestions have some sympathy at p5js, see the github issues discussion.



I've made good progress on my guide to algorithmic art, which aims at being accessible to artists who have never coded before, and who might even by shy of technology.


Simplest, Most Minimal Code

I try introduce coding (with p5js) in a very gentle and friendly talkative way.

Concepts like the canvas are familiar easy to get - they have a size and background colour, and you make marks on them, just like real life canvasses for painting on.

I then try to only introduce the simplest and most minimal code needed to get started making interesting compositions. I try to postpone looking at more complex code for as long as possible.

For example, to start with I avoid notions like RGB colour mixing, and just stick to the named CCS colours, like "red" and "pink".


This way, any technical or mathematical barriers are minimised and postponed,  helping to build up confidence and establish some good coding experience sooner.

But trying to do this I've run into things that make Processing/p5js harder than it needs to be. Here are three that I've found so afar.


Problem 1: Not So Simple Shapes

What's the simplest shape to use for learning Processing and exploring mathematical ideas?

A circle is a really simple - and friendly - shape. It only needs the location of its centre, and its size, to be specified. A line is more complicated because it needs the start and end location coordinates to be specified.


But Processing doesn't have a circle() instruction!

Instead it has an ellipse() command. The word ellipse is not as friendly a circle, and can seem scarily mathematical, likely losing some beginners a this early and unnecessary hurdle.

What's more, we have to explain that an ellipse with the horizontal and vertical diameters set to the same numbers ... is in fact a circle. Again, this is really too much to be expecting a reticent shy non-technical artist to be struggling with.

Processing would be so much more beginner friendly if it had a circle() instruction:

circle(10, 10, 5);

Instead of

ellipse(10, 10, 5, 5);

or even the 3-parameter version where the vertical and horizontal diameters are assumed to be the same:

ellipse(10, 10, 5);


This should be a really easy change to the p5js source code as circle() could simple call the existing ellipse(), an alias if you will.

What's the next simplest, most familiar shape? A square! But Processing doesn't have a square() instruction either.

Instead, Processing requires us to use the rect() instruction. It's not even immediately cleat that rect is for rectangle. If we're trying to keep technical and mathematical distractions away from new beginners, we shouldn't be asking them to remember that a rectangle with equal sides is a square, and that for some reason we use a truncated English word, rect, not rectangle.

Again, this should be really easy to implement in p5js's source code.


Problem 2: Loopy Loops

Repetition is an important concept in both art and in computer science. Repeating chunks of code is a very powerful thing to do.

How do we repeat some code 5 times in p5js?

for (var count = 0; count < 6, count = count + 1) {
  // do something;
}

Yikes! Try explaining that to a complete beginner! I do try, and even then, the following are conceptually painful and counter-intuitive.
  • the use of 6 when we want the code to be repeated 5 times, not 6
  • the use of a condition to continue, rather than an ending condition

A construct with a different idea such as "repeat .. until (condition)" would be far better and more intuitive. We all understand the plain English explanation "keep doing this until that happens". Instead we have "start here, keep going as long as this is true, and at every loop do this" .. bleurgh!

But we can do even better, simpler. Logo, an education focussed language designed in 1967, got it right with a simple REPEAT 5. That's it. That's a lesson in simplicity. From 1967.

It would be great to have that simple easy entry into loops with p5js, something like:

repeat 5 {
  // do something; 
}

It is true that a counter is very useful as we get more advanced. But then Python can show how that can be done really nicely too - for x in range(10), or for y in [2, 3, 5, 7]., where we can use the counters x and y inside the repeated code.

It should be easy to do easy common things - that's a great design philosophy.



Problem 3: Picking Random Numbers

Another important and very useful algorithmic concept is randomness. Using randomly chosen numbers to decide things like location, size, colour is interesting as a tool for composing images.


Look back at that last sentence - location, size, colour - these are all defined by whole numbers. Location is in terms of (whole) pixels, Size is in (whole) pixels in Processing. Colour is actually based on mixing red, green and blue light and the underlying system is based on a scale from 0-255 going up in whole numbers.

Yet we use random() to pick random numbers .. and they're floating point decimal numbers like 1.31, 3.28, 21.01 and so on. Not integers. So p5js has to round these to integers when used for location, size and colour

Because this rounding to whole numbers happens behind the scenes, I did try to ignore and avoid the issue altogether. Nobody would know that circle(random(100), random(100), random(20)) involved floating point numbers.

But we can't ignore it if we use randomness to throw dice, to decide whether we do something or not. Something like "throw a coin, if it's heads do this" .. or "throw a six sided die, and if it's six do this". That requires integers.

There isn't a function for randomly picking whole numbers. So we end up with:

if (int(random(6)) == 0) {
 // then do something;
}

That's uneccesary complexity and code.

Most languages and maths libraries have a random integer generator as a first class functon, a randint() would be good.

But even randint isn't the friendliest word. I propose that we keep the simplest case simple, and only incrementally adding complexity:

random(5) // pick a whole number from 0, 1, 2, 3, 4, 5
random(1, 5) // pick a whole number from 1, 2, 3, 4, 5

randomf(5) // floating point number in range 0 to 5, exclusive
randomf(1,5) // floating point number in range 1 to 5, exclusive

That would break backwards compatibility, so might be something for the next major version of p5js.