∞ Tannenbaum!

Source.

On the first Sunday of Advent, my siblings (13 & 15) and I got busy building Christmas trees – but not out of wood! We made generative art. You can see the result at 🎄.rixx.de (fallback for the less unicode-savvy: ootannenbaum.rixx.de), where you can generate your own random Christmas trees.

That's already pretty cool, in my admittedly biased opinion. What's even cooler though is how we got there: While I did all the typing, everything else was a collaboration. All the research, ideas, testing, calculations that went into this, roughly four hours of joint generative Christmas. What we did can work for others, too, so this is a writeup of what we did, how this works, and how to tackle generative processes like these. It's much easier than it may look if you haven't done something like this before!

Resources

First of all: You can look up all the details of how the Christmas tree is made. This file contains all the code, and it's heavily commentated (close to be its own blog post). The comments walk through the code and explain in plain English what's going on. If you leave out the ".en" in the URL, you'll find the same in German (which is the version we actually wrote).

If you haven't done anything remotely similar before, there is a great introduction in German by the equally great bleeptrack. This is a similar talk in English. You'll find more links and resources at the end of this page.

Prior knowledge

What level of prior knowledge do you need to build something like this? Don't worry, it's not all that much! A bit of perseverance or knowledge of a bit of any programming language or somebody who can program and is willing to help you should show you through. If you don't know anybody who can help you, look for local hackerspaces, programming courses, or ask around online when you hit stumbling blocks. Given concrete questions, there should be people around who can help.

For reference: my brother is 15, has learned some Java (and a tiny bit of HTML) at school, and had no problems following our progress. My sister is 13, and has no programming experience at all. She still understood what we did as well, especially the design and work on the parameters to get to a balanced result.

Process

When starting out with a generator like this, it's best to start simple: Think of the basic form hidden in what you're trying to do, and try to get it to show up in the live editor at sketch.paperjs.org. For example: to draw a rectangle, you can copy the code below and click the “run” button at the top:

var rectangle = new Path.Rectangle(new Point(100, 100), new Size(200, 300));
rectangle.fillColor = "red";
rectangle.strokeColor = "black";
rectangle.strokeWidth = 5;
rectangle.rotate(45)

As somebody who speaks English, you have an advantage, because this is pretty self-explanatory for you. It draws a rectangle at the coordinate (100, 100). Note that (0, 0) is the top left corner. The first number is the x value, which changes how far something is moved to the right, and the second number is the y value, which moves elements further down.

The rectangle takes up 200x300 pixels, and filled with red colour with a 5 pixel black border. Finally, we rotate it by 45°. Now try to play around with it a bit! You can read up on all the other possibilities in the documentation. Don't worry if it's hard to read at first, that's completely normal when approaching technical documentation. It's best to get started by reading the documentation for some part that you know already, to figure out the structure and language. For example, search for “rotate” to see the description of the rotation of an object.

For our Christmas tree, we started out by drawing triangles on top of each other. So we had to figure out the triangle coordinates, so that the point on top was centered between the other two. We started out on paper to make sure we understood the coordinate system, and then transfered our result to the paper.js editor. Then we played around with the numbers until we could draw first one, then several triangles.

Advice

The most important piece of advice is: proceed in very small steps. After each change, take a look at the results and if they match your expectations. The nice thing about a graphical project like this is that you can see if your code works as expected, and if it doesn't, the results are often very funny. More than once we generated trees that were unreasonably tiny, or huge, entirely transparent, or upside down!

Small steps could be: Draw one element. Draw another element. Add colour. Add a third element in a different colour. Change the width of the element. Make the width random. Make the amount of elements random. And so on.

The second most important piece of advice is: learn from others. The vaaaaaaaast majority of problems in this area have been encountered and presumably solved by others. It's good to try on your own at first – that way you learn more about the problem and its complexities. But if you can't find a solution soon, instead of growing frustrated: Search (e.g. on GitHub) for people with similar code and hopefully solutions. We did this a couple of times – for instance, the code to export the generated trees to PNG and SVG images is taken directly from a similar generator by bleeptrack. And now you can look up similar things in turn in our extensively commented code.

Randomness

After some time and experimenting, you'll end up with a drawing. Now you can add in the part that's the most fun: Randomness! It's not terribly easy, but a lot of fun, and will reward you with unexpected results.

JavaScript has a function named Math.random(), which returns a long number between 0 and 1. We built a small helper function which returns a random number between its parameters:

function randomNumber(min, max) {
  // This function produces a random number between min and max.
  // Min is inclusive, max is exclusive.
  return Math.floor(Math.random() * (max - min)) + min;
}

Feel free to copy this function, because building on this it's way easier to get started. Now you can do things like make your drawing between 100 and 300 pixels wide, or make it rotate by 45°-90°. You can also use this like dice or throwing a coin to choose between different features. We only draw a star on top of our Christmas tree for two out of three trees, by calling randomNumber(0, 3), and only drawing a star if the result is not 0.

Colour

The size of a drawing is the best first approach to randomness. It will make you think about some measurements some more: If parts of your drawing border a random part, they need to be positioned correctly, so you'll have to add some calculations for that. If this gets boring, you can also vary the colour.

Colours in paper.js and in many other digital systems are made up of three parts: red, green, and blue (the RGB system). These values are often on a scale from 0 to 255. paper.js uses values between 0 and 1 instead – but because I found it harder to reason about small numbers like that, we chose numbers in the [0, 255] range and divided them by 256, instead. This is how we determine the green colour of a tree:

var red = randomNumber(0, 100) / 256
var green = randomNumber(115, 255) / 256
var blue = randomNumber(40, 130) / 256
triangle.fillColor = new Color(red, green, blue)

So how do we find these colour ranges? How do we know that our red value should be between 0 and 100? If you search for Colourpicker in the search engine of your choice, you should find an interactive colourpicker, often as part of the search result page. If you move the colour point in the area that looks good to you, you can pay attention to the way the three values change, and which range is appropriate for each of them. This is way easier if you're not alone, by the way, because one of you can focus on moving the point in a good colour space, and the other can write down the values. Afterwards, you can finetune the colours, e.g. if you notice that your colours tend to come out too blue, you can reduce the upper limit of your blue range.

Further reading

And this is how you can get started with generative art! I hope this helps to get you (or people you know) started. Have fun!

This is a list of sources and further reading:

Setting up Datasette, step by step

To build a Datasette, just stack these three boxes on top of each other Source.

Last week, I showed off how I exported my Goodreads data to SQLite, and you can see the data at data.rixx.de. I use Datasette to make the data available to browse, query, plot, export, etc. Datasette is a brilliant project, because it makes data available to you in an interactive way, and allows you to share original data and analysis in a transparent bundle.

I think I deployed this Datasette to data.rixx.de in about five minutes – but that's because I have plenty of templates for the deployment of Python web applications, and I only copied two files and executed a couple of commands. Since I vividly remember being incredibly frustrated more often than I can count when I didn't have these templates, let me share the process with you.

Read more…

Goodreads → SQLite

Books! Disappearing into books is great, and I have well-founded suspicions that it's occasionally the only thing that keeps me fit for human company. I like to share my opinions of the books I read – mostly so that I can look it up in a couple of months or years, but also for the small-but-steady troupe of interested people. This usually takes the form of my monthly book blog post – but now, you can see the raw data, too!

Using goodreads-to-sqlite Source.

Read more…

Books: 2019-09

September was great – I finally found the time and energy to go back to reading. I finished Anathem, which kept me reading for a whole month in between attending and organising events. (That's why there was no blog post in August – “currently reading Anathem” didn't seem like enough content to go on).

This month was a good mix of a bunch of fiction that I loved (my first Jo Walton book, and getting back to Dorothy Sayers), non-fiction about emotions and about writing and software, and relatively few books I didn't enjoy. Also, some short stories that are available for free online.

Read more…

Books: 2019-07

July was a strange mix of books I absolutely adored, and books I didn't like at all. Also, I spent at least half of the month stuck in Dhalgren.

Read more…

Advice for a bad speaker

Speaking of which. Source.

The internet is full of advice on how to give good presentations, and convince an audience of whatever the speaker thinks it needs convincing of. In contrast, there is a remarkable lack of advice on how to give bad presentations or talks. Luckily, the German author Kurt Tucholsky sensed this need ninety years ago, and provided his advice for bad speakers (and, incidentally, good speakers, too).

Kurt Tucholsky was a brilliant German journalist, satirist, writer, and sometimes-poet in the 1920s and 1930s. The bulk of his work consists of contributions to the newspaper Weltbühne (“World Stage”) – he produced so many published texts that he wrote under a total of five names.

His writing is always to the point, cutting away unnecessary bulk and showing the reader the world as it really is, sometimes using a mirror. The trademark Tucholsky dry humor and sharp judgement is hard to translate, but I gave it a go anyways.

Read more…

Exporting Bookmarks from FBReader

Carrying all those books is something I sure don't miss. Source.

I read a lot, and I read a lot across different devices. Usually, I have at least a book open on my Kindle, and one on my mobile phone. I also like to highlight passages that make me laugh, or think, or that touch me in some way. I collect some of those quotes in a fortune(6) file for further usage: Put it on login shells, share it with friends, use it as a tongue-in-cheek Patreon reward.

The Kindle just drops your highlights into a plain text file with a very readable structure, which is very pleasant to work with. It turned out that my Android reader app, FBReader isn't nearly so cooperative, so this post documents how to extract the data from an unrooted phone.

Read more…

127.257 and other fun legacy IP addresses

I think I'm taking up a coat of arms. Source.

IPv6, the addressing scheme of the future, has been lacking adoption for decades now. Its complexity is an often cited reason: What's with the different kinds of addresses? What's fe80 supposed to be? And you can't even compare them properly, what with those pesky :: contractions!

Though … did you know that 127.257 is interpreted as a valid IPv4 address by programs such as ping and your browser? Let's look into this.

Read more…

Books: 2019-06

June was long, and hot, and not terribly filled with reading. I managed to read six books, and then I got stuck on Delany's Dhalgren. That one might take up a complete month …

Read more…

Mobile Zammad notifications with Pushover

Both for my standalone work and for pretalx.com, I use the Zammad ticket system. Sadly, it includes neither a mobile app nor a usable mobile layout – but since I usually have my phone around, even when my laptop sits in my backpack/the next room/somewhere else, I wanted to see new tickets on my phone as they come in. Dismissing tickets I can look up later isn't particularly stressful for me, and well worth the opportunity to react to urgent tickets as fast as possible.

Read more…

Using the Django shell with django-scopes

If you have a project where multiple tenants/users/groups share the same database, but should never interfere with one another, you're going to worry about making sure to prevent data leaks. I know, because I run this kind of setup with multiple projects, most notably pretalx, which uses Django.

As one measure of separating tenants, pretalx uses the new and wonderful django-scopes, which makes sure that models identified as scoped are only accessed in an active scope context. If this sounds interesting to you, please read the blog post introducing django-scopes – it explains the principles and limitations very well.

We started using django-scopes with pretalx, and we were pretty happy. Then, one day, I needed to look up some data that is not directly exposed in the frontend, for debugging purposes.

Read more…

Index HTTP statūs numeri

Last night, when talking with a friend, I quipped

Django est omnis divisa in partes tres, quarum unam incolunt moduli, aliam visus, tertiam templates, qui exempli gratia DTL aut jinja2 appellantur.

This is a modification of the opening sentence of Cæsar's “De Bello Gallico” regarding the Gallic war, where he explains that Gaul was at the time divided into three parts. Django, often described as a MVT (Model View Template) framework was a natural fit for this sentence and my late-night desire to find a practical application for my long and not always pleasant years of Latin.

Read more…

Books: 2019-05

May was a very generic month. Did the working, did the volunteering, did the reading. Noticed that I am unhappy and figured out what to do about it, as you do. I didn't read as many books as I'd like, and none of them stood out too much.

Read more…

Books: 2019-04

April was a travelling month: I spent all of five days at home, and the rest of the time I moved between Berlin, Copenhagen, Vienna, and Berlin again. While this was fairly stressful, it gave me plenty of time to read on the various trains and ferries I travelled on.

Read more…

A @context decorator for Django

Even the happiest Django developers with the most ardent love of class-based views can get tired of writing super().get_context_data(**kwargs), only to add a couple of already-implemented properties or methods to the template context.

django-context-decorator provides, as the name may hint at, a decorator named @context. You can wrap it around both methods and properties, including Django's cached property, and it will add the property's value or the method's return value to your template context:

Read more…