Shady is a simple programming language which lets you define images as functions from points in 2-d space to color values. Here is an example:
| Input | Output |
|---|---|
let redColor = rgb(1, 0, 0);
let blackColor = rgb(0, 0, 0);
let main(x, y) =
if x*x + y*y < 1 then
redColor
else
blackColor;
|
|
The image defined by the function main is rendered by the Shady interpreter. The virtual coordinate space for the rendered image ranges from (-1, -1) to (1, 1).
The Shady interpreter is written in Python and is available here. To download the source code, click here. The core interpreter is in evaluator.py. It is quite small and should be easy to read.
A Shady program consists of a sequence of let definitions. Let definitions can be used to define both values (like red, black) and functions (like main). The body of a let definition consists of a single expression terminated by a semicolon.
The primitives rgb, and rgba are constructors for color values. The color component values should be in the range [0, 1]. The primitives red, green, blue, and alpha are for accessing the individual components of a color value. All math primitives from Python's math module are available in Shady.
Ellipse. A simple ellipse.
| Input | Output |
|---|---|
let greenColor = rgb(0, 1, 0);
let whiteColor = rgb(1, 1, 1);
let ellipse(x, y) =
if (x*x + 2*y*y) < 1 then
greenColor
else
whiteColor;
let main = ellipse;
|
|
Rotated Ellipse. Images are just functions (from x,y to color). Hence functions which operate on images are nothing but higher-order functions. In this example, rotate is a higher order function which rotates the image img by theta radians.
| Input | Output |
|---|---|
let greenColor = rgb(0, 1, 0);
let whiteColor = rgb(1, 1, 1);
let ellipse(x, y) =
if (x*x + 2*y*y) < 1 then
greenColor
else
whiteColor;
let rotate(img, theta) =
lambda (x, y) =
img(x*cos(theta) - y*sin(theta),
x*sin(theta) + y*cos(theta));
let main = rotate(ellipse, 45 * pi/180);
|
|
Cosine curve. This example shows how a gradient can be drawn. Also note that the -1 to +1 range of x has been effectively translated into -π to +π, by multiplying x by pi before computing the cosine.
| Input | Output |
|---|---|
let redColor(u) = rgb(u, 0, 0);
let blackColor = rgb(0, 0, 0);
let main(x, y) =
if y < cos(x*pi) then
redColor(abs(y))
else
blackColor;
|
|
Rosenbrock function. This is a visualization of the Rosenbrock function. In general, 3-d surfaces of the form z = f(x, y) can be visualized by representing z as color.
| Input | Output |
|---|---|
let square(u) = u * u;
let rosenbrock(x, y) = 100*square(y - x*x) + square(1-x);
let maxZ = 5;
let clipScale(u) = if u > maxZ then 0 else u/maxZ;
let redColor(f) = rgb(f, 0, 0);
let main(x, y) = redColor(clipScale(rosenbrock(x, y)));
|
|
Rosenbrock Quantized. This is a color quantized visualization of the previous example. The quantization has been done using the floor function.
| Input | Output |
|---|---|
let square(u) = u * u;
let rosenbrock(x, y) = 100*square(y - x*x) + square(1-x);
let maxZ = 5;
let clipScale(u) = if u > maxZ then 0 else u/maxZ;
let redColor(f) = rgb(floor(f*maxZ)/maxZ, 0, 0);
let main(x, y) = redColor(clipScale(rosenbrock(x, y)));
|
|