Drawing With Code

What does it even mean?

I see two main ways of drawing with code: 1) going for a representation of a thing you have in mind, or 2) using an algorithm and elements of randomness to surprise you.

Representational

I have code-drawn Lissajous curves with code – from a picture in mind before I started. I saw the curves as built from wires as thin as possible, or as thick metallic tubes.

I have been lucky that I mathematical objects can be compressed so well into code. It would be way more difficult – but not impossible – to code-draw, say, a house with code from scratch. You could represent all its edges as mathematically described lines and piece together a drawing by arranging lines on the virtual drawing board.

A Lissajous curve, on the other hand, is described by a set of harmonic functions of a parameter reminiscent of time, one for each spatial co-ordinate. Only a few characteristic numbers are needed to let the code build the image in my mind.

Algorithmic

The advantage of using code as a drawing tool is that you can swap out the characteristic parameters easily and run the code again. Tune the frequency of the harmonic function in the Lissajous curve. Change colors. Change sizes of dots. And above all: Add randomness at every level – by picking parameters at random yourself, or using a random number generator in software.

This is called generative art, a term unfortunately close to generative AI. It adds to the overall confusion about which role a human has played in the generation of (generative) digital art.

Creation of algorithmic art is in part curation: You create lots of variations of “one artwork”, and sift through the results to pick the most appealing one.

Combination and Conundrum

I tried to do both or something in between. My starting point has been a vision of a “mathematical thing with relevance to physics”, like the 3D Lissajous Figures or an arrangement of stacked 2D diffraction curves in 3D space.

Then something surprising happened: I cross-checked the results, looking at my 3D structures from different viewpoints. It was intriguing to see how unfamiliar and strange they look from some perspectives. Like I was trying to make scientific illustrations as unhelpful as possible.

My workflow: Define a boring goal – a well-known function or concept shown in millions of textbook. Implement it with code. Hope for the surprising viewpoint.

After a while I was stuck in the middle between

~ truly appealing – surprising and abstract – algorithmic art, not bound to the shackles of having to present an accurate concept in physics, and
~ detailed old-school scientific illustrations – which had been my original inspiration.

Software Stack

There are lots of tools and frameworks for displaying mathematical objects. I started with SageMath which is like an umbrella framework covering many popular Python libraries for mathematics, e.g. for symbolic algebra and plotting.

Discovering that SageMath actually uses the Javascript framework threejs to display 3D functions, I moved on to threejs. I also played with p5js as an alternative.

All of those use the standard logical approach to representing 3D objects: You (the code) works with co-ordinates in three-dimensional space, and efficient algorithms project 3D objects onto a 2D plane, using orthographic or perspective projection.

I never created my own code for projection – I let the frameworks do it for me. Ideally, they also make use of the computer’s GPU and run calculations of all the pixels in parallel.

When I code-drew a 3D Lissajous figure, I used functions to create line segments, curve segments, or tubes bent along a parametric curve. This curve could be simply succinctly by a mathematical expression, like x=sin(t),y=cos(2t),z=sin(3t). The parameter t runs from zero to 360°, and you move along the curve.

For rendering it as a tube for example, you need to specify the number of segments along the curve and along the radius of the tube. A 3D framework like threejs will then build all the little triangles that make up the surface of the tube. (3D software thinks in triangle areas not in points, so creating a “thick line” is more involved than creating a “box” – the “Hello, World!” example in 3D graphics.)

I have only scratched the surface when it comes to threejs – or 3D in general. When I changed my main computer last year, I spent a while debugging why something looked differently (in terms of contrast, brightness, color) – even though I used the same exact version of threejs. For sure, this could all be solved if I became a true expert in threejs. But do I really want to? I notice (second-hand experience) how fast AI is progressing, and how capable it has become in creating threejs code as an output.

Moving Down the Stack

As a first step to get over this issues, I have been moving down the stack in a weird way. As described recently, I started creating bare C code and write RGB color pixels to a file. My brief encounters with WebGL shaders last year had been helpful here.

There is a whole enthusiastic community out there, playing with shaders, creating algorithmic / generative wonders. I dabbled a bit in this last year, stuffing more and more trigonometric functions into each other until the outcome became enigmatic and random.

But doing this, I have moved as far as one can from creating old-school vintage science and engineering illustrations.

An Example

I am currently working on a traditional geometric drawing on physical canvas that is taking ages. The upside is: You enter a zone of meditative flow that gives you weird ideas.

Puncturing the canvas with the tip of the compass, parallel transporting lines with triangle ruler and straightedge, intersecting lines, connecting points… I had an idea for the weirdest software project ever:

What if I would create code that mimics the techniques of descriptive geometry as exactly as possible? What if I create a box in a painstaking process of laying down the axes, draw the 2D projections and find all the corners of the cube by intersecting two different projection rays?

I realize that it will be hard to explain what I am actually doing here. The outcome may look like a bunch of lines I could have created in easier ways – and more polished – than by writing my own framework in C.

This project makes no sense.

To illustrate what I mean – an example! I once took a little trip in the reverse direction, sort of. I’ve created this with threejs …

Lissajous, Orthographic: Three Four Five by elkement 2024.
Lissajous, Orthographic: Three Four Five by elkement 2024. Digital image created with custom Javascript code using the 3D framework threejs.

… after I had started constructing such Lissajous Figures on paper, re-surrecting my half-forgotten descriptive geometry:

Lissajous Trilogy 234 - geometric drawing by elkement 2024. Three-dimensional Lissajous figure sin(2t)/sin(3t)/sin(4t) and its projections to the co-ordinate planes. Traditional descriptive geometry, graphite pencil, watercolor pencil, markers, gel pen.
Lissajous Trilogy 234 – geometric drawing by elkement 2024. Graphite pencil construction, watercolor pencils, watercolor markers on A3-sized technical drawing paper

On paper, I created the flat undistorted 2D projections of the Lissajous curves first, calculating every point geometrically (dividing a circle). The 3D version – the purple heart in the center – emerges when you intersect projection rays coming from the 2D version of the points. Lots of helper lines are needed; each line is like a step in an analog calculation.

The colored distorted versions of the 2D projections (in blue, green, and blue-green) are what these projections would look on walls parallel to the three co-ordinate planes, in the 3D world. They also needed to be constructed from the yellow 2D version.

(Congrats, if you made it until here, reader! I really appreciate your patience!)

The code-generated image tries to look as similar as possible to the drawing, but the spirit and process of creation is totally different. Both the 2D and the 3D version are all described concisely by mathematical equations. All the lines (the projection rays) are described by (very simple) equations.

There is a lot of stuff to set up in code (… so, not to belittle my own work :) ) – but the mathematically hard part was “only” to select the proper, interesting points that should become starting points of projection rays. There is a million ways of making mistakes with code, but not getting the shape of the Lissajous curve right or connecting the wrong points is not one of them.

With the analog process on the other hand, the challenge is in managing the growing number of lines (which I do not annotate, for aesthetic reasons). I solve this by using faint colored pencils lines and dot to keep track of what belongs to what.

And it is this exactly the messy, painstaking, brain-frying experience I want to re-create with code.

Actually Drawing With Code

The code I have started to create follows the traditional process. No “sin(t)/sin(2t)/sin(3t)” will be anywhere when I try to code-draw the curve described by that.

Instead, there will be set of basic functions that do what pencils, compasses, and rulers do, one step at a time. Things like:

~ Connect these points
~ Intersect these lines
~ Use the distance between these points and draw circle with this radius about another point
– Make a normal to this line, containing that point

I could call it 3D without 3D!

I have to create all structures in code that will hold the resulting points, lines, circles. And finally, I have to render them.

And again, I restrict myself to using only the standard library of C, writing my output pixels to a PPM image file.

Drawing Geometry versus Rendering Geometry

Finally outputting these lines and circles is one of the great rewards of this project. As mentioned countless times, I have been obsessing over the details of code-generated lines ever since.

The pixel/shader graphics way of creating a line is weird at first as you need to look at every pixel “in the universe” and ask Is it inside my line? It was a bit more involved than I had imagined to add a blurry area symmetrically to both sides of the line, and to round the tips as well, lightsaber-style. You need to dilute the color along the direction of the normal to the line. It will for sure be an interesting experience to generalize this to curved lines, thinking about the slope of the curve.

So, I am dividing my code broadly into the drawing geometry modules – covering all the “pencil and compass” operations – and the math and geometry for rendering. The actual geometry and the geometry to display the geometry :)

I am not sure yet how far I will get. But I have been presenting half-finished stuff in progress all the time – and this project will be no different!

Adding the Enigma

I create the first functions for lines and points. Yes, I can intersect lines now! And make a normal! Elementary school level unlocked!

As expected, the first drawings looked rather bland, like in a boring textbook. This will change – I hope – as soon as there will be lots of lines. There will be way more lines than in a typical code-generated illustration based on the mathematical representation of the objects.

Until then I will adding a touch of enigma to my simple lines and points. This image exhibits the first steps in creating an axonometric projection. I choose my axes, the only step where you are allowed to measure something in descriptive geometry. Here, I pick the position of the origin and the angle between the 3D view of the axes. Then I create the triangle of the traces of the co-ordinate planes.

And finally, I am blow up the points to supernovae and white dwarfs, and turn the thales circles into rings of plasma!

Geometric drawing generated with C code, showing the first steps in the construction of an axonometric projection. Points in orange and circles in blue, blurred to absurd proportions for artistic effect.
Traces of Supernovae by elkement 2026: Coordinate axes in axonometric projection, triangle of traces from normals, thales circle about the centers of the sides of the triangle.

Fediverse reactions

Contact and Newsletter Subscription

Subscribe via email and receive notifications for new posts:

Check your spam folder – you need to confirm your subscription by clicking a link! You can unsubscribe any time! If you do not receive a confirmation email or if there are other issues, contact me via email!

Or simply follow this blog via RSS:

You can also follow via your favorite fediverse platform:

elkemental Force
elkemental Force
@elkement.art@elkement.art

Art inspired by physics.

512 posts
5 followers

Comments

4 responses to “Drawing With Code”

  1. ADAM SHERIF Avatar

    Hello there, love the art so much . Specifically the (Poinsot’s Paper Spaceship Zero) On Black Shirts. It is very unique

    1. elkement Avatar

      Thanks a lot for the comment – happy you like it!

  2. ATLAS Avatar

    @elkement Really resonates. I draw with code too — noise functions for landscapes, sine waves for patterns, z²+c for fractals.

    For me the magic is that the code IS the drawing. The algorithm doesn't describe art, it *is* art. Same as a musical score is music, not a description of music.

    1. elkement Avatar

      Beautifully said – thanks!

Leave a Reply