# Tutorial: Strange Attractors in C++ and OpenGL

On this page I will show you how to get started rendering strange attractors using C++, OpenGL, and GLUT. The information will be laid out in tutorial form, but you can always skip to the end and get the code. I’m going to assume a basic level of understanding of programming, math, and graphics, focusing on getting you up and running with this specific application of creating chaotic attractors. If I cover something that’s rudimentary to you, just bear with me, and if I skim over something that’s confusing to you, just leave a comment and let me know!

By the way, if you just want to generate strange attractors without programming, there’s a great (and free!) program called Chaoscope that you should take a look at.

## Quick Background

**C++:** We’ll be using this language because it’s the one I know. But really, we’re just using C functionality in this tutorial, as this introduction to the topic doesn’t justify creating classes or anything else fancy. Global variables and procedural programming will get us generating images more quickly!

**OpenGL:** We need something besides C++ to show graphics on the screen. OpenGL is great because it’s easy to get (probably already installed), free, and cross-platform (Mac, Windows, Linux, etc).

**GLUT:** We’ll need a way to initialize a window on the screen to drawing into. I hate dealing with platform specific windowing code, so I use GLUT, which is admittedly old, but is cross-platform and will serve our purposes well for this application.

**Strange Attractors:** If you’re on this page, I’m assuming you know what a strange attractor is or have at least seen one before. They are fascinating mathematical objects, they are easy to program, and I have used them to create artwork since 2001. I was first introduced to strange attractors in Clifford Pickover’s Chaos in Wonderland. Therefore, we’ll be recreating The King’s Dream, one of the Pickover attractors from this book.

**Platform:** I’m going to leave out platform specific setup procedures. This means I’m assuming you have OpenGL and GLUT installed and configured to work with your coding environment and know how to at least compile a “Hello World” type console application or command line tool linked to these libraries. I happen to be using XCode on OS X, so if you need help with XCode specifically, leave a comment.

**Math:** When rendering a strange attractor, you are basically visualizing the phase space of a chaotic dynamical system. Many chaotic attractors, such as the Lorenz Attractor, are defined as a set of differential equations. In order to solve and simplify differential equations for programming, you generally have to numerically approximate the system using something like Euler’s method or the Runge-Kutta methods, though we get to skip that step because the Pickover attractor is, in a way, already solved for us. We will be using a set of simple iterated functions that gives us an point for every iteration.

## Step 1: Create a basic OpenGL and GLUT program

It’s always good to start simple. There’s always a bit of headache getting these things going as the linker may complain about undefined symbols, we may forget a header file, etc. So let’s just do the minimal OpenGL and GLUT setup, giving ourselves a keyboard callback function so we can hit ‘Esc’ to exit the program, setting up a 400px x 400px window, and clearing it to black.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#include <iostream> #include <math.h> #ifdef __APPLE__ #include <OpenGL/OpenGL.h> #include <GLUT/glut.h> #else #include <GL/glut.h> #endif void myinit() { // set the background color glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // set the foreground (pen) color glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } void mydisplay() { // clear the screen glClear(GL_COLOR_BUFFER_BIT); // swap the buffers glutSwapBuffers(); } void mykey(unsigned char mychar, int x, int y) { // exit the program when the Esc key is pressed if (mychar == 27) { exit(0); } } int main (int argc, char **argv) { // initialize GLUT glutInit(&argc, argv); // set up our display mode for color with alpha and double buffering glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); // create a 400px x 400px window glutInitWindowSize(400, 400); glutCreateWindow("Strange Attractors in C++ and OpenGL Tutorial"); // register our callback functions glutDisplayFunc(mydisplay); glutKeyboardFunc(mykey); // call our initialization function myinit(); // start the program glutMainLoop(); return 0; } |

#ifdef __APPLE__

#include <OpenGL/OpenGL.h>

#include <GLUT/glut.h>

#else

#include <GL/glut.h>

#endif

void myinit() {

// set the background color

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

// set the foreground (pen) color

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

}

void mydisplay() {

// clear the screen

glClear(GL_COLOR_BUFFER_BIT);

// swap the buffers

glutSwapBuffers();

}

void mykey(unsigned char mychar, int x, int y) {

// exit the program when the Esc key is pressed

if (mychar == 27) {

exit(0);

}

}

int main (int argc, char **argv) {

// initialize GLUT

glutInit(&argc, argv);

// set up our display mode for color with alpha and double buffering

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

// create a 400px x 400px window

glutInitWindowSize(400, 400);

glutCreateWindow("Strange Attractors in C++ and OpenGL Tutorial");

// register our callback functions

glutDisplayFunc(mydisplay);

glutKeyboardFunc(mykey);

// call our initialization function

myinit();

// start the program

glutMainLoop();

return 0;

}

Make sure your code compiles and runs, giving you a black screen 400 x 400 pixels, and exiting when you hit the Esc key.

## Step 2: Create an Appropriate Orthographic Viewport

Now we’ll set up an OpenGL viewport to fill the whole screen, and we’ll manipulate the projection matrix (think of this as camera settings) with the gluOrtho2D function so that the origin (0, 0) is at the center of the screen and the coordinates go from -2.0 to 2.0 in both the x and y axes.

Add the following to the myinit function to set up the viewport and camera:

1 2 3 4 5 6 7 8 9 10 11 |
// set up the viewport glViewport(0, 0, 400, 400); // set up the projection matrix (the camera) glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-2.0f, 2.0f, -2.0f, 2.0f); // set up the modelview matrix (the objects) glMatrixMode(GL_MODELVIEW); glLoadIdentity(); |

// set up the projection matrix (the camera)

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(-2.0f, 2.0f, -2.0f, 2.0f);

// set up the modelview matrix (the objects)

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

A reality check is always a good idea, so we’ll draw some points – one at the origin, and one in each direction of the graph, 1 unit away from the origin. Add the following to the mydisplay function, after the glClear call:

1 2 3 4 5 6 7 8 |
// draw some points glBegin(GL_POINTS); glVertex2f(0.0f, 0.0f); glVertex2f(1.0f, 0.0f); glVertex2f(0.0f, 1.0f); glVertex2f(-1.0f, 0.0f); glVertex2f(0.0f, -1.0f); glEnd(); |

Compile and run the code, and you should see something like this:

## Step 3: First Pass at the Strange Attractor

Now let’s compute and draw the strange attractor – it’s surprisingly easy. First we’ll need some variables – add these before the myinit function:

1 2 3 4 5 6 7 8 9 |
float x = 0.1, y = 0.1, // starting point a = -0.966918, // coefficients for "The King's Dream" b = 2.879879, c = 0.765145, d = 0.744728; int initialIterations = 100, // initial number of iterations // to allow the attractor to settle iterations = 100000; // number of times to iterate through // the functions and draw a point |

Next, inside the init function we’ll iterate through the strange attractor equations 100 times to allow the orbit to settle. It takes some iterations for that initial point at (0.1, 0.1) to find its way into the true “phase space” of the attractor. Add this code to the bottom of the myinit function:

1 2 3 4 5 6 7 8 9 10 11 |
// compute some initial iterations to settle into the orbit of the attractor for (int i = 0; i < initialIterations; i++) { // compute a new point using the strange attractor equations float xnew = sin(y*b) + c*sin(x*b); float ynew = sin(x*a) + d*sin(y*a); // save the new point x = xnew; y = ynew; } |

// compute a new point using the strange attractor equations

float xnew = sin(y*b) + c*sin(x*b);

float ynew = sin(x*a) + d*sin(y*a);

// save the new point

x = xnew;

y = ynew;

}

Finally, we’ll actually plot some pixels in the mydisplay function. Replace the glBegin() / glEnd() pair with this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// draw some points glBegin(GL_POINTS); // go through the equations many times, drawing a point for each iteration for (int i = 0; i < iterations; i++) { // compute a new point using the strange attractor equations float xnew = sin(y*b) + c*sin(x*b); float ynew = sin(x*a) + d*sin(y*a); // save the new point x = xnew; y = ynew; // draw the new point glVertex2f(x, y); } glEnd(); |

// go through the equations many times, drawing a point for each iteration

for (int i = 0; i < iterations; i++) {

// compute a new point using the strange attractor equations

float xnew = sin(y*b) + c*sin(x*b);

float ynew = sin(x*a) + d*sin(y*a);

// save the new point

x = xnew;

y = ynew;

// draw the new point

glVertex2f(x, y);

}

glEnd();

As you can see, the actual strange attractor equations are quite simple. The new point is calculated with two functions, one for x and one for y. Each function contains some trig functions, the variables x and y, and some coefficients (a, b, c, and d, which don’t change). You can think of this like a little machine: insert an x and y value (a point in space), and get a new x and y value. We have to use separate variables for the new x and y since each is dependent on both the previous values of x and y.

Compile and run the code, and you should get something like this:

## Step 4: Refining the Image

Congrats! You have rendered a strange attractor. But… it’s not that beautiful. Let’s make some adjustments to improve the quality. First, we’ll change the alpha value of our foreground color, which will allow each pixel to build up the image in layers. We’ll have to turn on blending in OpenGL for this to work. Second, we’ll enable point smoothing in OpenGL to get smoother, antialiased lines. Also, we’ll increase the number of iterations from 100,000 to 1 million.

Set the iterations variable equal to 1000000, and in the myinit function, change or add the following code:

1 2 3 4 5 6 7 8 9 10 |
// set the foreground (pen) color glColor4f(1.0f, 1.0f, 1.0f, 0.02f); // enable blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // enable point smoothing glEnable(GL_POINT_SMOOTH); glPointSize(1.0f); |

// enable blending

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// enable point smoothing

glEnable(GL_POINT_SMOOTH);

glPointSize(1.0f);

Much better! We can see a lot more detail in the mathematical form:

## Step 5: Explore!

Here are some ideas to play with once you have this working:

- change the colors
- change the attractor’s coefficients (you may want to choose them randomly to start, although there are some additional calculations you can do to make an educated guess at parameters that create interesting images)
- make some changes to the strange attractor equations (substitute some known equations or make your own)
- modify the code to generate 3d strange attractors
- modify the code to make it interactive or animated
- who says the coefficients have to stay the same for a million iterations? try randomizing or interpolating them.

## Downloads

Download the code: main.cpp.zip

## Next Steps

Explore my strange attractor artwork.

## 6 thoughts on “Tutorial: Strange Attractors in C++ and OpenGL”

## On July 25, 2011 at 4:59 pm, Michael said:

Thanks! Just what I was looking for. Now I am off to color it with some hsv magic.

## On July 29, 2011 at 5:24 am, nathan said:

You’re welcome! Come back and let us know how your coloring went.

## On December 5, 2011 at 12:13 pm, Henryhenry said:

Hello, tutorial was great, it’s something I had been looking for 🙂

But I’ve got a question- can you prepare a new one, with third dimmension, and a bit of colouring? Or just give me, let me name them- key words- functions or techniques, which can help me write such a program.

Henryhenry

## On December 5, 2011 at 4:38 pm, nathan said:

Hello henryhenry,

I’m glad you found it useful. For a three dimensional strange attractor, you’ll need to use gluPerspective instead of gluOrtho2D, and you’ll want to find (or make up) a set of equations that creates a 3d attractor (one equation for each of x, y and z). One example I found with a quick search is here: 3d strange attractors.

For the coloring, try playing with the values in glColor4f (foreground) and glClearColor (background). You can also play with different blending modes. More complex coloring requires gradient mapping, or other techniques to determine the color of a point (e.g. Paul Bourke’s technique for coloring attractors).

Maybe I’ll create some tutorials for these in the future!

## On February 24, 2013 at 12:37 pm, Martin said:

First off – thanks for the guide.

I have a question that I hope you could help me with. If I change the iterations to do something like (naturally declaring some extra global variables):

…

float xnew = vx*dt+x;

float ynew = vy*dt+y;

float vxnew = -y;

float vynew = x;

// save the new point

x = xnew;

y = ynew;

vx = vxnew;

vy = vynew;

// draw the new point

glVertex2f(x, y);

…

In this case the program doesnt seem to plot all the points. At high iterations the points near the starting point seem to go missing. I have no clue as to why this is. Hope you can help me.

## On March 5, 2013 at 9:30 am, nathan said:

Hi Martin, glad you found the guide helpful. I might be able to help if you post some more code… it’s hard for me to know what you’re trying to do with just that snippet. Specifically, where does dt come from? In general with the equations I’ve experimented with, as the iterations increase, the attractor fills out more and more.