Carbon Dioxide Camp's Time Travel Booth

Though I list it here on WPS, this was in fact a completely collaborative project; artists in four cities across the U.S. made substantial contributions. The booth was assembled for the first time on the playa, Burning Man 2009. It was our camp's theme that qualified us for placement. It was wildly successful, open to random passers-by on the street, and ran reliably unattended for most of a week.

Erika Wanenmacher, Eve Andree Laramee, Benjiii A. Geary, Emily Montoya (DJ DirtGirl), Mykl Wells, David Forbes, and myself were major contributors. I am writing this from memory in 2012, if I left someone out PLEASE tell me! Apologies in advance for my poor memory!

We're all tight friends to begin with. Most of the collaboration was done in our camp mailing list and individual email correspondence. We did try to arrange the work such that sub-projects could be done with minimal interdependence, for practical reasons, though there was certainly a huge communication load.

Project documentation is scattered all over:

Basically it was an interactive set that led participants through a scripted performance. Not clearly visible in the photos are the props that led participants through the machine and set up their expectations (the obtuse legal disclaimer document is below).

Basically -- upon entering the dark booth, lights came on dimly and a soundtrack played; "welcome to the CO2 Camp time travel booth...", some scary disclaimers were delivered, and as the participant was asked to make a selection, the so-far-inert control panel lit up, and waited for a selection to be made. (Alas, only one selection works ("SOLD OUT")...). Upon making the choice, the time travel sequence began: a sci-fi sound track, flashing lights, spinning disks... "ding!" you ahve arrived, please exit the booth... total performance was approximately two minutes (if I recall...)

The script was executed by an Arduino microcontroller. The sole inputs to the controller were a mat switch on the ground (to know when someone was in the booth) and the push-button switch to select the destination. The Arduino also played the soundtrack, and drive the lights and displays. It was powered by a fat gel cell and a solar panel.


The script was done entirely collaboratively, with artists in three cities: Santa Fe, Tucson, Los Angeles. I turned the script into Arduino code. it is this process that is interesting from a technical artist's point of view.

We viewed this project as a sculptural "set" for a performance, with a strict script that had to meet fairly stringent requirements: Burning Man is a tough crowd! Physically demanding, but most importantly, people are out walking and biking, hot and dirty (or cold and dirty, at night) and no one has the patience for delicately subtle or bland amusements -- things need to grab attention, be understood immediately (in an environment of wild sensory stimulation) and not require a lot of focus to follow or untangle.

We used a shared spreadsheet, on Google Docs, to create a step-by-step "script" of actions and inputs. The spreadsheet was used strictly as a "structured document", a brutally straightforward table of actions leading to subsequent actions, 1, 2, 3, ... We did mental walk-throughs -- and physical ones standing on the floor -- of how the performance would play out, literal step by step. We could not assume people would stay for the entire performance. Neither did we want anyone to overstay their welcome.

The spreadsheet, with numbered steps (see below) kept things simple and clear. And rigorous enough to turn into software.

Once we had the script worked out to our satisfaction -- everyone had individually run through it and we could find no further problems -- Emily and Benjiii created the soundtrack. The soundtrack consisted of two sections, before and after the (time travel destination) selection was made. At selection time the script paused and waited for the button to be pressed. Before and after that point everything ran with fixed timing; the run-throughs ensured that the pacing and timing all made sense and kept people's attention.

I then took the script/spreadsheet, and added columns to it, that were the software/electrical dependencies and actions needed to complete the steps. We called the visitor/participant the "perp" as in perpetrator.

(There is a column for time spent at each step, here shown only as place-holder round numbers; the script here never got updated with the exact times. By that point changes were being made in the software itself, also shown below.)

The script lines are also embedded in the software itself. The software has more complexity per-step than the script; these are all software failsafes, eg. what if someone walks out during a performance? In that case, the Arduino turns things off and returns to the beginning, awaiting some to enter the booth.

Here is a (very low quality!) brief Quicktime movie of the user experience.

Arduino code

Below is the Arduino code that executes the script above:

 /* Carbon Dioxide Camp 2009
Time Travel Booth controller

  tom jennings

  02 Sep 2009  on-playa tweak of spiral speeds; BADSW stopped working!
  09 July 2009 running, ruined only one Arduino so far
  02 july 2009
  27 june 2009 new

These values correspond to the sound track positions for various
utterances in the script; see the script for details. These numbers are
milliseconds we should delay until some utterance is made, before we execute 
some action. 
// Point in Track A where it says "please make a selection"
#define T_SELECT 29000

// from GO press to done TEMPORAL SHIFT
#define T_SHIFT 46000

// from start of SHIFT to arrived
#define T_ARRIVED 6000

// from start of ARRIVED to exit
#define T_EXIT 25000

#define SOLDOUTC 50

// analogWrite max.
#define AWMAX 255

// The SD-25 takes time to boot, and misses pulses before this?
#define SD25BOOT 6000

// Spiral motor stuff. It doesn't always start right
// at the low speed we want it to run at; so there's a 
// start voltage that is output for a second or so to get
// it started then we drop to the low speed.
#define SPSTART 255
#define SPFAST 180
#define SPSLOW 120

// CCFL and lamp brightnesses. These are ad hoc, on a fully 
// charged battery. 255 (AWMAX) is full on.
#define SODIM 5
#define SOMED 50
#define SOBRIGHT 100

#define PADIM 40
#define PAMED 140
#define PABRIGHT 255

// Overhead LED lamp.
#define LAMPDIM 2
#define LAMPMED 25
#define LAMPBRIGHT 128


MATSW    The mat switch
LAMP     Booth internal overhead lamp
SPIRAL   Rotating spiral thingie
BADSW    Any of the "sold out" selections
GOSW     The good ("mystery") selection
RUNPWR   Power for devices on during entire performance
PANEL    Power for main selection panel
SD25TRKA Pulse to begin Track A
SD25TRKB Pulse to begin Track B/end Track A

#define MATSW 2
#define LAMP 3
#define SOLDOUT 5
#define SPIRAL 6
#define BADSW 7
#define GOSW 8
#define RUNPWR 9
#define PANEL 10
#define SD25TRKA 11
#define SD25TRKB 12

unsigned char state;
unsigned char gosw, badsw, matsw; // momentray switch state machines
unsigned char lightlevel;
unsigned long T, matswT, timer;
unsigned int soldoutC;           // kludge to run the SOLD OUT sign

unsigned char debugstate = 255;
unsigned char debugsws = 0;

/* How long without a mat switch press we go to call the booth "empty" (mS). */
#define MATSWTIME 6000

/* How often we execute the state machine, mS */
#define INTERVAL 10

/* GO and BAD switch state machine mask and definition of "pressed". */
#define SWMASK 3
#define SWPRESS 2

/* Set shit up, make sure all outputs are OFF! */
void setup (void) {

  Serial.begin (9600);
  Serial.println ("");
  Serial.println ("");
  Serial.println ("");

// pins not configured default to INPUT.
  pinMode (LAMP, OUTPUT);  digitalWrite (LAMP, 0);
  pinMode (SOLDOUT, OUTPUT);  digitalWrite (SOLDOUT, 0);
  pinMode (SPIRAL, OUTPUT);  digitalWrite (SPIRAL, 0);
  pinMode (RUNPWR, OUTPUT);  digitalWrite (RUNPWR, 0);
  pinMode (PANEL, OUTPUT);  digitalWrite (PANEL, 0);
  pinMode (SD25TRKA, OUTPUT); digitalWrite (SD25TRKA, 0);
  pinMode (SD25TRKB, OUTPUT); digitalWrite (SD25TRKB, 0);

// Give an external indication that the booth is alive.

  digitalWrite (SOLDOUT, 1);           // light up console,
  delay (1000);                        // for one second
  digitalWrite (SOLDOUT, 0);
  randomSeed (analogRead(0)); 

  readsw(); readsw();                  // init switch state machines
  Serial.println ("setup() done.");

// Schedule the first state machine execution.
  state= 0;

  T= millis();
  matswT= T + MATSWTIME;

void loop (void) {

  int n;
// Only execute code every TT milliseconds.

  if (millis() < T) return;    // not time yet
  T= millis() + INTERVAL;      // is, sked next.

  readsw();                    // read the switches

// This code runs only while the booth is occupied. "Occupied" means
// someone has stepped on the mat switch within MATSWTIME mS.
// We maintain a timer that is reset every time someone steps
// on the switch. When it times out, the booth is assumed to be empty
// and this code resets.
// (Switch statemachines have LSB == 0 if the switch IS pressed or 
// WAS pressed.)
  if (matsw != SWRELEASE) matswT= T;       // reset timer when stepped in
  if ((T - matswT > MATSWTIME)
     && (state > 1)) state= 0;             // reset machine upon timeout

// Now execute the state machine.

  switch (state) {

    case 0:
      Serial.println ("0: Elvis has left the building.");
      digitalWrite (LAMP, 0);        // turn out booth lighrs,
      digitalWrite (PANEL, 0);       // turn off the console,
      digitalWrite (SOLDOUT, 0);     // turn off SOLD OUT,
      digitalWrite (RUNPWR, 0);      // turn off audio, outside light
      digitalWrite (SD25TRKA, 0);    // (set to play no audio)
      digitalWrite (SD25TRKB, 0);
      digitalWrite (SPIRAL, 0);
      state= 1;                      // now go wait for someone to enter
// STEP 1: Wait for someone to enter the booth.
    case 1:
      if (T - matswT < MATSWTIME) state= 2;

// STEP 2: Someone entered the booth. Start track A ("welcome 
// to the CO2 time travel booth...") Also sets the time
// we will wait for the next step.
    case 2:
      Serial.println ("2: Perp entered booth; start Track A.");
      digitalWrite (RUNPWR, 1);      // booth is now occupied
      lightlevel= LAMPBRIGHT;        // max. booth brightness
      delay (500);                   // delay for pacing
      digitalWrite (LAMP, lightlevel);// interior LAMP on,

      delay (SD25BOOT);              // let SD-25 boot up,
      digitalWrite (SD25TRKA, 1);    // play track A,
      analogWrite (SPIRAL, SPSTART); delay (500);
      analogWrite (SPIRAL, SPSLOW);  // run the spiral slowly
      state= 3;
      timer= T + T_SELECT;           // next step waits for "Make a selection..."

// STEP 3: Wait a suitable interval for the sound track to get to the
// place where it says "make a selection". Let's slowly dim the booth LAMP
// in the last second before we light up the console. THIS CODE IS A CROCK.
    case 3:
      if (timer - T < 5000) {        // in the last second,
        lightlevel -= (5000/LAMPBRIGHT); // dim LAMP
        analogWrite (LAMP, lightlevel);
      if (T > timer) {
        Serial.println ("4: Perp can now press GO");
        analogWrite (LAMP, LAMPDIM);
// Turn the panel on full to start the lamps under low battery conditions.
        analogWrite (PANEL, AWMAX); delay (150);
        analogWrite (PANEL, PABRIGHT); // panel on
        state= 4;
      soldoutC= 0;                   // next step, SOLD OUT is off

// STEP 4: Wait for the perp to press the big GO switch eg. make
// a selection. (Track A is playing and if you wait long enough, loops.)
// If the perp presses a "sold out" switch make a bad noise and
// flash the SOLD OUT sign.
    case 4:   
      if (gosw == SWPRESS) state= 5;   // wait for GO switch
//        Serial.print ("badsw"); Serial.print (badsw, DEC);
      if (badsw == SWPRESS) {          // SOLD OUT item,
        analogWrite (SOLDOUT, SOBRIGHT); // light the sign
        soldoutC= SOLDOUTC;            // set timer,
      if (soldoutC && ! --soldoutC) analogWrite (SOLDOUT, 0);

/*    soldoutC= SOLDOUTC; // SOLD OUT item
      if (soldoutC) {
        analogWrite (SOLDOUT, soldoutC ? SOBRIGHT : 0);


// STEP 5: Perp has pressed the GO switch. Begin sound track B
// ("You have selected...") and set the timer for the next step.
    case 5:
        Serial.println ("5: Perp made a selection; begin Track B.");
        analogWrite (SOLDOUT, 0);               // in any case, SOLD OUT now off
        delay (500);                            // feels better when the panel stays on this long
        analogWrite (PANEL, PADIM);

        digitalWrite (SD25TRKA, 0);             // stop track A
        delay (25);                             //
        digitalWrite (SD25TRKB, 1);             // start track B,
        timer= T + T_SHIFT;                     // next wait is for SHIFT
        state= 6;

// STEP 6: Wait until sound track "WE HAVE TEMPORAL SHIFT",
// then set the next timer and go to the next step.
    case 6:
      if (T > timer) {
        timer= T + T_ARRIVED;                    // next wait is for ARRIVED
        state= 7;
        Serial.println ("7: WE HAVE TEMPORAL SHIFT.");
        analogWrite (PANEL, PADIM);

// STEP 7: Woo woo! Time travel has side effects! LAMP flash, 
// booth vibrates, etc. Sound track is making time-travel sounds.
    case 7:
      analogWrite (SPIRAL, SPFAST);
      analogWrite (PANEL, random(PADIM, AWMAX)); // erratic lighting
      n= random (0, AWMAX); if (n < LAMPBRIGHT) n= 0;
      analogWrite (LAMP, n);                    // erratic lighting
      analogWrite (SOLDOUT, random(SODIM, SOMED)); // erratic lighting
      delay (150);                              // this fucks up timing...

      if (T > timer) {                          // when over,
        timer= T + T_EXIT;
        state= 8;
        Serial.println ("8: Arrival.");
        analogWrite (SPIRAL, 0);
        analogWrite (PANEL, AWMAX); delay (150);
        analogWrite (PANEL, PADIM);
        analogWrite (LAMP, LAMPDIM);
        analogWrite (SOLDOUT, 0); 

// STEP 8: ARRIVAL. The sound track is making ambient sounds. Make
// the booth passively dim. When the timer times out the sound track 
// should have just said "please exit the booth now."
    case 8:
      if (T > timer) {
        Serial.println ("9: Turn off everything; induce perp to exit.");
        analogWrite (SPIRAL, 0);
        analogWrite (PANEL, 0);              // console off, 
        analogWrite (LAMP, 0);               // booth off,
        digitalWrite (SD25TRKB, 0);
        digitalWrite (RUNPWR, 0);
        state= 9;

// STEP 9: Wait for perp to leave the booth. Make it dark. Note that
// this step does nothing; basically we wait for the beam to time out,
// which is caught outside the state machine.
    case 9:

// Watch for switch presses. This is a two-bit
// state machine that shift the current state of the switch
// in on the right. Un-pressed, the switch reads 1; when pressed
// it reads 0. Therefore, the moment the switch is pressed the
// two bits read binary "10" (SWPRESS).

void readsw (void) {

  gosw <<= 1;
  gosw |= digitalRead(GOSW);
  gosw &= SWMASK;

  badsw <<= 1;
  badsw |= digitalRead(BADSW);
  badsw &= SWMASK;

  matsw <<= 1;
  matsw |= digitalRead(MATSW);
  matsw &= SWMASK;


Last and least, some pictures of the control panel construction. The booth itself is more interesting, and documented elsewhere, I hope, such as on the Gallery above.

It's not pretty inside, but it was rugged -- it had to operate in 100+F degree weather, resist infiltration of the fine sifty carbonate salt dust of the playa (which is electrically conductive, corrosive, and generates static electricity) and resist the predations of it's audience.