ArtRay: a Haskell ray tracer

Depth-of-field simulation

Depth-of-field simulation

Getting and Running ArtRay

You can download a ZIP file or clone the Github repository to get the code. A script,, is provided in the distribution for building ArtRay. It will produce an executable ar. The executable takes two arguments: the input scene and the output image. Input scenes are in ArtRay format, which is detailed below, and output images are always in PNG format. Any scene-related options are set in the input file, and cannot be set on the command line.

ArtRay format

Note, this section will likely be out of date by the time I finish writing it. For more up-to-date information, see the documentation distributed with ArtRay.

The ArtRay format reads like Haskell code, but is easy to understand even for those who are unfamiliar with Haskell syntax. There are a number of samples distributed with the code in the scenes/ directory. I recommend looking at these files to understand the syntax of the format, as it's very simple.

The primitives in ArtRay are colors, vectors, lists and scalars; every object in a scene is built from these. Colors are expressed as a triple of fractional values between 0 and 1, corresponding to the red, green and blue components of that color. Colors must be surrounded by parentheses, like so:

background = (1, 0.5, 0)

That statement would set the background color to an orange (100% red, 50% green and 0% blue).

Vectors are also represented by three values, but the syntax is different. Unlike colors, they shouldn't be surrounded by parenthesis, and the numbers are separated by spaces instead of commas. They must also be proceeded by the word Vec3, like so:

location = Vec3 -10 0 10

Lists are surrounded by square brackets, and elements in a list are separated by commas. Newlines can also be placed between elements in lists, but are optional. An example of a list is shown below:

colors = [
  (0, 1, 0),
  (1, 0, 0)

Shadow simulation

Shadow simulation


The top-level object in any scene is a Scene, which has five fields, background, options, geom, lights and viewer, each of which are required. The current parser requires that these fields be in order -- future versions of ArtRay will eliminate this requirement.

Scene Options

An ArtRay scene has an (optionally empty) list of options in its options field. At the moment there are only two options: GlobalAmbient and Antialiased. Sample syntax is shown in the snippet below:

options = [
  GlobalAmbient (0.4, 0.4, 0.4),
  Antialiased 4

GlobalAmbient takes the global ambient color as it's one argument (this will be explained further in the Lighting section below).

Antialiasing is done using MSAA. The Antialiased option takes a single argument, which corresponds to the number of subpixels per dimension that will be sampled, so an argument of 4 corresponds to MSAAx4, or 16 subpixels per pixel. In general, a higher subpixel count means better antialiasing and longer runtime.


The geometry of a scene is a list of geometric primitives. ArtRay only supports two kinds of geometric primitives: spheres and half-planes. Each kind of primitive has a material associated with it.

Spheres are defined by a center (a vector), a radius (a scalar) and a material. The syntax for a red sphere with center (10, 0, 0) and a radius of 1 is shown below:

Sphere {
  center = Vec3 10 0 0,
  radius = 1.0,
  material = ColorMaterial (1, 0, 0)

Planes are defined by a vector on the plane and the normal vector of the plane. Note that the "plane" primitive is actually a half-plane: it will only reflect light from the side the normal points towards. This means that if you use a more complex material for it (i.e., anything that isn't a ColorMaterial) only one face will be lit correctly. To create a plane that reflects light from both sides, add another plane to the scene with the same position vector and a negated normal vector. The syntax for a blue yz-plane where x=8 is as follows:

Plane {
  pnorm = Vec3 -1 0 0,
  point = Vec3 8 0 0,
  material = ColorMaterial (0, 0, 1)

Lights and Materials

There are two lighting models available in ArtRay. One has no shading and no lighting at all -- a primitive that uses this lighting model has one color over the entire body, and is flatly shaded. To use this lighting model on a primitive, assign a ColorMaterial to that primitive. ColorMaterial takes one argument, which is the color of the primitive.

For realistic lighting, ArtRay uses Phong reflectance. This decomposes light and materials into three portions: specular, diffuse and ambient. ArtRay simplifies this model somewhat -- instead of allowing each light to contribute to ambient, there is just one global ambient applied to the scene. This is set using the GlobalAmbient option described in the Options section. Each light has two parameters: speclight, the specular color of the light, and difflight, the diffuse color of the light. Each light also has a location stored in the loclight field. PhongMaterial, has four fields: specular, diffuse, ambient, and phongexp. specular, diffuse and ambient are the corresponding color components of the material. phongexp is the Phong exponent of the material -- this effects the size of the specular highlights on the object. The higher the exponent, the brighter and smaller the highlights will be. The geometry and lights sections from a scene that uses Phong lighting is shown below:

geom = [
  Sphere {
    center = Vec3 10.0 0.0 0.0,
    radius = 1.0,
    material = PhongMaterial {
      specular = (1.0, 1.0, 1.0),
      diffuse = (0.0, 0.7, 0.0),
      ambient = (0.0, 1.0, 0.0),
      phongexp = 4 

lights = [
  PhongLight {
    speclight = (1.0, 1.0, 1.0),
    difflight = (1.0, 1.0, 1.0),
    loclight = Vec3 6.0 4.0 0.0 


The Viewer of the scene defines the "camera" viewing the scene. It is parameterized by four vectors: location, u, v and f. location is a vector which is the location of the imaginary camera. u, v and f are slightly more difficult concepts to describe. In order to determine what in the scene the camera can see, ArtRay simulates an actual camera of sorts. This camera has a "focal length" f. In reality, f is a vector that points from the camera's location to the location of the center of it's "image plane" -- think of this as the direction that the camera is looking. v is the vector that points up, and it's length determines how much of the scene the camera can see vertically. Making v longer means that more of the scene can be seen in the vertical direction. u is the horizontal equivalent to v, and should be orthogonal to v. Again, the longer u is, the more can be seen in the horizontal direction.

Note that the wider you make u and v, or the shorter f is, the more "barrel warp" you will see in the image. If you want to see more without distortion, you will need to move the camera itself further away from the scene.