Learning Python and being creative. Making art with code.

What do words look like as colours? What would Shakespear’s sonnets look like as colours? This mini project renders text as colours using Python and saves them in a grid as an image.

All titles of Shakespear’s sonnets

What is this?

As it turns out, it wasn’t so easy. Like most developers, I started looking for a JS library that already did this, wishful thinking as it’s not really that useful. I thought this from Charlie Coleman was pretty cool, but I was more interested in showing a more granular representation. A color for each word, no matter how many letters or words.

Why? And Why Python?

Why Python? Well, JavaScripts was good, but it was slow and getting slower. I heard python was really fast and I planned to be throwing hundreds of words at it.

Planning

  • The colours created from the words should get the colour values using every letter, not just the first few.
  • The ‘canvas’ on which the colours sit should be adjustable, I should be able to print a huge image of a whole book, or a small image of my name.
  • The final rendering should be an image, not a web view.

An example: the letters ‘Abcd’ as a word

# position   0   1   2   3   4   5  ...
alphabet = ["a","b","c","d","e","f",...]

As RGB values only need 3 numbers, we use the remaining letters to increase the values of the numbers representing the first three letters. So…

  • Word is ‘Abcd’
  • Values are [0,1,2]
  • Remaining values are [3]
  • Add [3] to the first value (0).
Some small word examples

What happens with large words?

  • Word is ‘Abcdefghij’
  • Values are [0,1,2]
  • Remaining values are [3,4,5,6,7,8,9]
  • For each of the remaining values, go through the initial values and add them to the 3 RGB values one at a time so,
  • Remaining value 3 is added to val[0]
  • Remaining value 4 is added to val[1]
  • Remaining value 5 is added to val[2]
  • Remaining value 6 is added to val[0]
  • Remaining value 7 is added to val[1]
  • Etc

That zip cycle function saved me a lot of trouble:

for i, j in zip(cycle(range(len(colors))), additions):
colors[i] += j
colors[i] = int(colors[i])

There are also some other nice bits in the code to ensure the values don’t exceed [255,255,255], really it was just something that came up that needed some thought. You’ll notice that in the list above, the initial values could be small, like [0,1,2]. So they are multiplied by (255 /26) or (value limit/letters in alphabet). This gives a better base to add values to. [0,9.8,19.6].

As we start adding the leftover values, they are multiplied, to ensure we are don’t tip over 255, we divide by 26, for values above 3 but lower than 6. Then for words with more than 6 letters, they get really small values by dividing more. (x * n /26)(1/26). Here is that function:

Adding to the initial RGB values, getting smaller and smaller as the word grows
(left) E.E Cummings — I carry your heart with me (right) William Carlos Williams — The Red Wheelbarrow

What about large text inputs?

The King James Bible — In squares

Drawing, Plotting, and Some Note-taking.

Working out the plotting of the word squares

So why so many notes and working out? Well in the code at this point we have a list (potentially huge) of colour values, e.g.:

[[45,65,221],[36,187,35],[66,84,224]...]

This list needs to be looped over and a square drawn for each item using the values as colours. The hard part here was drawing the squares in a horizontal line and dropping down a row when we hit the end. But how do we know we are at the end? What size should the drop-down value be?

Here is how that’s done with 5 words as an example:

  • Given I know how many squares I have (5) I work out what ‘grid’ I will need. For example, 5 words would require a 3 x 3 grid.
  • Now I know each row and column will consist of three squares, and I have a canvas size of 1000. 1000 / 3 will be the width and height of each inner square. And also the amount of distance I need to shift my drawing points horizontally and vertically.
  • To find what ‘grid’ I need, I created a function (grid(n,m)) that takes the list of words and a list of squared numbers and works that out:
Calculating the grid

Finally, all that left is the most confusing iteration I have ever written:

Drawing the squares.

All in all, this started as a bit of fun, but I ended up learning a lot about Python and drawing with code (something I would love to do more of). One really nice feature is that the canvas can be huge and your squares a few millimetres.

Developer, I also make art and listen to techno https://www.kevinhowbrook.com/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store