tachyon.in / kssreeram / shady

Introducing Shady

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).

Get Shady

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.

Syntax and Primitives

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.

More Examples

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)));