Generative art with Python — gradients

 Sat, 18 Feb 2023 17:15 UTC

Generative art with Python — gradients
Image: CC BY 4.0 by cybrkyd


My challenge was to write a script to generate random images which I will use as background images on my blog. Whilst these images can be relatively quick and easy to manually create, I wanted to have a little fun and see what the randomly-generated output would look like.

I settled on two-colour linear gradients generated via a Python script. The images are output in SVG format and the image consists of a combination of two random colours (rgb 0-255) in a random linear gradient.

The SVG output

I first needed to learn how a gradient is mapped in a SVG. Basically, there are two points, x and y, meaning a straight line, declared via a <linearGradient> node. Then, there are <stop> nodes which declare the colour at certain positions along the gradient. Let’s call this “the fade” where the two colours mix; I’m looking for a smooth, soft blend at the transition point.

With a simple, two-colour gradient in mind, the output I was looking for was similar to this:

SVG Gradient generated using Python

The SVG code is relatively straightforward:

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1400 400">
<rect width="1400" height="400"/>
<defs>
<linearGradient id="a" x1="22%" y1="31%" x2="9%" y2="74%">
<stop offset="0%" style="stop-color:rgb(81,169,147);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(204,238,41);stop-opacity:1" />
</linearGradient>
</defs>
<rect fill="url(#a)" width="1400" height="400"/>
</svg>

The Python script

Using random, two classes are required; one to randomise the points x and y and the other to randomise the colours, specified as RGB. Because we need two colours, we need to set two lines (x1:y1 & x2:y2).

The rest is fairly self-explanatory.

#!/usr/bin/env python3

import random

class rand():
	def __str__(self):
		return str(random.randint(0, 100))

class rgbset():
	def __str__(self):
		return str(random.randint(0, 255))
		
ran = rand()
rgb = rgbset()

svg1 = '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1400 400">\n<rect width="1400" height="400"/>\n<defs>\n'
svg2 = '<linearGradient id="a" x1="'+str(ran)+'%" y1="'+str(ran)+'%" x2="'+str(ran)+'%" y2="'+str(ran)+'%">\n'
svg3 = '<stop offset="0%" style="stop-color:rgb('+str(rgb)+','+str(rgb)+','+str(rgb)+');stop-opacity:1" />\n'
svg4 = '<stop offset="100%" style="stop-color:rgb('+str(rgb)+','+str(rgb)+','+str(rgb)+');stop-opacity:1" />\n'
svg5 = '</linearGradient>\n</defs>\n<rect fill="url(#a)" width="1400" height="400"/>\n</svg>'

# write to file
file = open("grad.svg", "w")
file.writelines("%s%s%s%s%s" % (svg1,svg2,svg3,svg4,svg5))
file.close()

Some interesting results can be obtained by randomising the stop offset= output but, as mentioned above, my personal preference is for a nice, smooth transition.

GitHub

The script is on GitHub along with a shell script to output 10 gradients at a time. Some further examples of the output are also available.