block by dribnet 169c75cd6803e5662a11a6b3303b4d67

confetti characters

Full Screen

PS2 MDDN 342 2016

confetti character set

A paramaterized letterform. Haphazard arangment and spinning transitions reminds me of confetti. This version will cycle through words or update based on keypresses from the keyboard.

index.html

<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/p5.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/addons/p5.dom.js"></script>
    <script language="javascript" type="text/javascript" src=".purview_helper.js"></script>
    <script language="javascript" type="text/javascript" src="sketch.js"></script>
    <style>
        body   { padding: 0; margin: 0; }
        .inner { position: absolute; }
        #controls {
            font: 300 12px "Helvetica Neue";
            padding: 5;
            margin: 5;
            background: #f0f0f0;
            opacity: 0.0;
            -webkit-transition: opacity 0.2s ease;
            -moz-transition: opacity 0.2s ease;
            -o-transition: opacity 0.2s ease;
            -ms-transition: opacity 0.2s ease;
            z-index: 1;
        }
        #controls:hover { opacity: 0.9; }
    </style>
</head>
<body style="background-color:white">
    <div class="outer">
        <div class="inner" id="controls" height="500">
            <table>
                <tr>
                    <td>Pos1</td>
                    <td id="slider1Container"></td>
                </tr>
                <tr>
                    <td>Tilt1</td>
                    <td id="slider2Container"></td>
                </tr>
                <tr>
                    <td>Pos2</td>
                    <td id="slider3Container"></td>
                </tr>
                <tr>
                    <td>Tilt2</td>
                    <td id="slider4Container"></td>
                </tr>
                <tr>
                    <td>Pos3</td>
                    <td id="slider5Container"></td>
                </tr>
                <tr>
                    <td>Tilt3</td>
                    <td id="slider6Container"></td>
                </tr>
                <tr>
                    <td>
                        <hr>
                    </td>
                </tr>
                <tr>
                    <td>Letter</td>
                    <td id="selectorContainer"></td>
                </tr>
                <tr>
                    <td>Draw Mode</td>
                    <td id="modeContainer"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="buttonContainer"></td>
                </tr>
            </table>
        </div>
        <div>
            <div id="canvasContainer"></div>
        </div>
    </div>
    <pre>
        <p id="output">
        </p>
    </pre>
</body>

letters.json

{
  "A": {
    "box1": {
      "position": -174,
      "tilt": -47
    },
    "box2": {
      "position": -104,
      "tilt": -4
    },
    "box3": {
      "position": -121,
      "tilt": 58
    }
  },
  "B": {
    "box1": {
      "position": -191,
      "tilt": -90
    },
    "box2": {
      "position": -54,
      "tilt": -45
    },
    "box3": {
      "position": -12,
      "tilt": 6
    }
  },
  "C": {
    "box1": {
      "position": -163,
      "tilt": -84
    },
    "box2": {
      "position": -191,
      "tilt": 163
    },
    "box3": {
      "position": 0,
      "tilt": -27
    }
  },
  "D": {
    "box1": {
      "position": -163,
      "tilt": -84
    },
    "box2": {
      "position": -16,
      "tilt": 17
    },
    "box3": {
      "position": -12,
      "tilt": -27
    }
  },
  "E": {
    "box1": {
      "position": 0,
      "tilt": 0
    },
    "box2": {
      "position": 0,
      "tilt": 0
    },
    "box3": {
      "position": 0,
      "tilt": 0
    }
  },
  "F": {
    "box1": {
      "position": -200,
      "tilt": -90
    },
    "box2": {
      "position": -200,
      "tilt": 180
    },
    "box3": {
      "position": -200,
      "tilt": -180
    }
  },
  "G": {
    "box1": {
      "position": -200,
      "tilt": 147
    },
    "box2": {
      "position": -153,
      "tilt": 180
    },
    "box3": {
      "position": -200,
      "tilt": 169
    }
  },
  "H": {
    "box1": {
      "position": 0,
      "tilt": -90
    },
    "box2": {
      "position": -170,
      "tilt": -90
    },
    "box3": {
      "position": 0,
      "tilt": -11
    }
  },
  "I": {
    "box1": {
      "position": -51,
      "tilt": -90
    },
    "box2": {
      "position": -51,
      "tilt": -90
    },
    "box3": {
      "position": -51,
      "tilt": -90
    }
  },
  "J": {
    "box1": {
      "position": -33,
      "tilt": 96
    },
    "box2": {
      "position": -121,
      "tilt": 24
    },
    "box3": {
      "position": -23,
      "tilt": 5
    }
  },
  "K": {
    "box1": {
      "position": -149,
      "tilt": -156
    },
    "box2": {
      "position": -121,
      "tilt": 134
    },
    "box3": {
      "position": -128,
      "tilt": 96
    }
  },
  "L": {
    "box1": {
      "position": -96,
      "tilt": -87
    },
    "box2": {
      "position": -121,
      "tilt": 99
    },
    "box3": {
      "position": -128,
      "tilt": -180
    }
  },
  "M": {
    "box1": {
      "position": -104,
      "tilt": -77
    },
    "box2": {
      "position": -89,
      "tilt": 90
    },
    "box3": {
      "position": 40,
      "tilt": 55
    }
  },
  "N": {
    "box1": {
      "position": 37,
      "tilt": 52
    },
    "box2": {
      "position": -89,
      "tilt": 90
    },
    "box3": {
      "position": 37,
      "tilt": 93
    }
  },
  "O": {
    "box1": {
      "position": 37,
      "tilt": 52
    },
    "box2": {
      "position": -89,
      "tilt": 90
    },
    "box3": {
      "position": -100,
      "tilt": 141
    }
  },
  "P": {
    "box1": {
      "position": -82,
      "tilt": -87
    },
    "box2": {
      "position": -121,
      "tilt": 166
    },
    "box3": {
      "position": -114,
      "tilt": 153
    }
  },
  "Q": {
    "box1": {
      "position": 37,
      "tilt": 52
    },
    "box2": {
      "position": -89,
      "tilt": 90
    },
    "box3": {
      "position": -100,
      "tilt": -166
    }
  },
  "R": {
    "box1": {
      "position": -107,
      "tilt": -87
    },
    "box2": {
      "position": -104,
      "tilt": -147
    },
    "box3": {
      "position": -160,
      "tilt": -180
    }
  },
  "S": {
    "box1": {
      "position": -200,
      "tilt": 144
    },
    "box2": {
      "position": -16,
      "tilt": 17
    },
    "box3": {
      "position": -30,
      "tilt": -21
    }
  },
  "T": {
    "box1": {
      "position": -51,
      "tilt": -180
    },
    "box2": {
      "position": -51,
      "tilt": 2
    },
    "box3": {
      "position": -51,
      "tilt": -90
    }
  },
  "U": {
    "box1": {
      "position": 0,
      "tilt": -90
    },
    "box2": {
      "position": -170,
      "tilt": -87
    },
    "box3": {
      "position": 0,
      "tilt": -39
    }
  },
  "V": {
    "box1": {
      "position": -125,
      "tilt": -81
    },
    "box2": {
      "position": -139,
      "tilt": -87
    },
    "box3": {
      "position": 5,
      "tilt": -39
    }
  },
  "W": {
    "box1": {
      "position": -104,
      "tilt": 43
    },
    "box2": {
      "position": -89,
      "tilt": 90
    },
    "box3": {
      "position": -89,
      "tilt": 131
    }
  },
  "X": {
    "box1": {
      "position": -61,
      "tilt": -156
    },
    "box2": {
      "position": -128,
      "tilt": 134
    },
    "box3": {
      "position": 40,
      "tilt": 36
    }
  },
  "Y": {
    "box1": {
      "position": -191,
      "tilt": -90
    },
    "box2": {
      "position": -54,
      "tilt": -45
    },
    "box3": {
      "position": -177,
      "tilt": -81
    }
  },
  "Z": {
    "box1": {
      "position": -2,
      "tilt": 2
    },
    "box2": {
      "position": -16,
      "tilt": -27
    },
    "box3": {
      "position": -191,
      "tilt": -180
    }
  },
  "0": {
    "box1": {
      "position": 37,
      "tilt": 52
    },
    "box2": {
      "position": -168,
      "tilt": 103
    },
    "box3": {
      "position": -140,
      "tilt": 158
    }
  },
  "1": {
    "box1": {
      "position": -9,
      "tilt": -87
    },
    "box2": {
      "position": -30,
      "tilt": 87
    },
    "box3": {
      "position": -44,
      "tilt": 93
    }
  },
  "2": {
    "box1": {
      "position": -9,
      "tilt": 17
    },
    "box2": {
      "position": -149,
      "tilt": -166
    },
    "box3": {
      "position": -200,
      "tilt": 134
    }
  },
  "3": {
    "box1": {
      "position": 5,
      "tilt": -8
    },
    "box2": {
      "position": 9,
      "tilt": -2
    },
    "box3": {
      "position": 5,
      "tilt": 5
    }
  },
  "4": {
    "box1": {
      "position": -100,
      "tilt": -180
    },
    "box2": {
      "position": -114,
      "tilt": 125
    },
    "box3": {
      "position": -3,
      "tilt": 90
    }
  },
  "5": {
    "box1": {
      "position": -200,
      "tilt": 163
    },
    "box2": {
      "position": -12,
      "tilt": 5
    },
    "box3": {
      "position": -30,
      "tilt": -21
    }
  },
  "6": {
    "box1": {
      "position": -65,
      "tilt": -36
    },
    "box2": {
      "position": 6,
      "tilt": -5
    },
    "box3": {
      "position": -3,
      "tilt": -14
    }
  },
  "7": {
    "box1": {
      "position": -12,
      "tilt": -5
    },
    "box2": {
      "position": -44,
      "tilt": -49
    },
    "box3": {
      "position": -128,
      "tilt": 131
    }
  },
  "8": {
    "box1": {
      "position": 5,
      "tilt": -30
    },
    "box2": {
      "position": 16,
      "tilt": 33
    },
    "box3": {
      "position": -40,
      "tilt": 49
    }
  },
  "9": {
    "box1": {
      "position": -146,
      "tilt": 150
    },
    "box2": {
      "position": 52,
      "tilt": 11
    },
    "box3": {
      "position": 23,
      "tilt": 91
    }
  }
}

sketch.js

// these are DOM elements
var main_canvas;
var pos1_slider;
var tilt1_slider;
var pos2_slider;
var tilt2_slider;
var pos3_slider;
var tilt3_slider;
var sel_char;
var sel_mode;

// constant for initialization
var canvasWidth = 960;
var canvasHeight = 500;

// this is the variable that holds all character objects
var letterParams = null;
var letterParamsDebug = {
  "A": {
    "box1": {
      "position": -174,
      "tilt": -47
    },
    "box2": {
      "position": -104,
      "tilt": -4
    },
    "box3": {
      "position": -121,
      "tilt": 58
    }
  },
  "B": {
    "box1": {
      "position": -191,
      "tilt": -90
    },
    "box2": {
      "position": -54,
      "tilt": -45
    },
    "box3": {
      "position": -12,
      "tilt": 6
    }
  },
  "C": {
    "box1": {
      "position": -163,
      "tilt": -84
    },
    "box2": {
      "position": -191,
      "tilt": 163
    },
    "box3": {
      "position": 0,
      "tilt": -27
    }
  },
}

// the sliders will write to this location
var debugLetter = "9";

// Handy string of all letters available
var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

// DrawMode can be "solo" or "alphabet"
var initialDrawMode = "exhibit";
var curDrawMode;

// these variables are used for animation
var soloCurLetter = "1";
var soloPrevObj = null;
var soloIsAnimating = false;
var soloNumAnimationFrames = 60;
var soloCurAnimationFrame = 0;

var chosenLettersDebug = ["C", "C", "C", "C", "C", "C", "C", "C"];
var chosenLetters = ["C", "O", "N", "F", "E", "T", "T", "I"];
var chosenPrevObjs = [null, null, null, null, null, null, null, null];
var chosenIsAnimating = [false, false, false, false, false, false, false, false];
var chosenNumAnimationFrames = 30;
var chosenCurAnimationFrame = [0, 0, 0, 0, 0, 0, 0, 0];
var curChosenLetter = 0;

var lastKeyPressedTime;
var secondsUntilSwapMode = 15;
var lastWordSwappedTime;
var isSwappingWords = true;
var secondsPerWord = 8;
var curSwapWord = 0;
var swapWordsDebug = [
  "CCCCCCCC",
  "AAAAAAAA",
  "BBBBBBBB"
]
var swapWords = [
  "CONFETTI",
  "ACTUALLY",
  "EXPECTED",
  "PROPERTY",
  "ADDITION",
  "FOLLOWED",
  "PROVIDED",
  "ALTHOUGH",
  "HAPPENED",
  "QUESTION",
  "AMERICAN",
  "INCREASE",
  "RECEIVED",
  "ANYTHING",
  "INDUSTRY",
  "RELIGION",
  "BUILDING",
  "INTEREST",
  "REMEMBER",
  "BUSINESS",
  "INVOLVED",
  "REQUIRED",
  "CHILDREN",
  "NATIONAL",
  "SERVICES",
  "COMPLETE",
  "ORGANIZE",
  "SOUTHERN",
  "CONSIDER",
  "PERSONAL",
  "STANDARD",
  "CONTINUE",
  "PLANNING",
  "STRENGTH",
  "ALPHABET",
  "POSITION",
  "STUDENTS",
  "DECISION",
  "POSSIBLE",
  "SUDDENLY",
  "DIRECTLY",
  "PRESSURE",
  "THINKING",
  "DISTRICT",
  "PROBABLY",
  "TOGETHER",
  "ECONOMIC",
  "PROBLEMS",
  "TRAINING",
  "EVIDENCE",
  "PROGRAMS"
]

function preload() {
  // sometimes when debugging, letterParams is initialized locally
  if(letterParams == null) {
    letterParams = loadJSON('letters.json');
  }
}

function make_interface() {
  // create two sliders
  pos1_slider = createSlider(-200, 200, 0);
  tilt1_slider = createSlider(-180, 180, 0);
  pos2_slider = createSlider(-200, 200, 0);
  tilt2_slider = createSlider(-180, 180, 0);
  pos3_slider = createSlider(-200, 200, 0);
  tilt3_slider = createSlider(-180, 180, 0);

  sel_char = createSelect();
  for (var i = 0, len = letters.length; i < len; i++) {
    sel_char.option(letters[i]);
  }
  sel_char.changed(letterChangedEvent);

  sel_mode = createSelect();
  sel_mode.option("alphabet");
  sel_mode.option("solo");
  sel_mode.option("exhibit");
  sel_mode.changed(modeChangedEvent);
  sel_mode.value(initialDrawMode);

  button = createButton('show data');
  button.mousePressed(buttonPressedEvent);

  // var button2 = createButton('random data');
  // button2.mousePressed(randomData);
  pos1_slider.parent('slider1Container');
  tilt1_slider.parent('slider2Container');
  pos2_slider.parent('slider3Container');
  tilt2_slider.parent('slider4Container');
  pos3_slider.parent('slider5Container');
  tilt3_slider.parent('slider6Container');

  sel_char.parent('selectorContainer');
  sel_mode.parent('modeContainer');
  button.parent('buttonContainer');
  // button2.parent(buttonContainer);

  dataObjectToSliders(letterParams[debugLetter])
}

function setup () {
  // create the drawing canvas, save the canvas element
  main_canvas = createCanvas(canvasWidth, canvasHeight);

  // rotation in degrees (more slider friendly)
  angleMode(DEGREES);
  curDrawMode = initialDrawMode;

  var now = millis();
  lastKeyPressedTime = now;
  lastWordSwappedTime = now;

  // position each element on the page
  main_canvas.parent('canvasContainer');

  if(initialDrawMode != "exhibit") {
    make_interface();
  }
}

// Generate a random set of letters.
// This was useful to generate placeholders.
// They are output to the html.
function randomData() {
  var o = {};
  for (var i = 0, len = letters.length; i < len; i++) {
    var obj = {}
    obj["box1"] = {};
    obj["box1"]["position"] = Math.round(random(-20, 20));
    obj["box1"]["tilt"] = Math.round(random(-30, 30));
    obj["box2"] = {};
    obj["box2"]["position"] = Math.round(random(-20, 20));
    obj["box2"]["tilt"] = Math.round(random(-30, 30));
    obj["box3"] = {};
    obj["box3"]["position"] = Math.round(random(-20, 20));
    obj["box3"]["tilt"] = Math.round(random(-30, 30));
    o[letters[i]] = obj;
  }
  var text = select('#output');
  var json = JSON.stringify(o, null, 2);
  text.html(json)
}

// Take all values from the slider and generate a data object
// that represents one character in the alphabet.
function sliderToDataObject() {
  var obj = {};
  obj["box1"] = {};
  obj["box1"]["position"] = pos1_slider.value();
  obj["box1"]["tilt"] = tilt1_slider.value();
  obj["box2"] = {};
  obj["box2"]["position"] = pos2_slider.value();
  obj["box2"]["tilt"] = tilt2_slider.value();
  obj["box3"] = {};
  obj["box3"]["position"] = pos3_slider.value();
  obj["box3"]["tilt"] = tilt3_slider.value();
  return obj;
}

// Given a data object representing a character, adjust
// the sliders appropiately.
function dataObjectToSliders(obj) {
  pos1_slider.value(obj["box1"]["position"]);
  tilt1_slider.value(obj["box1"]["tilt"]);
  pos2_slider.value(obj["box2"]["position"]);
  tilt2_slider.value(obj["box2"]["tilt"]);
  pos3_slider.value(obj["box3"]["position"]);
  tilt3_slider.value(obj["box3"]["tilt"]);
}

// Funcation called when DOM letter selector is changed
function letterChangedEvent() {
  var item = sel_char.value();
  dataObjectToSliders(letterParams[item]);
}

// Funcation called when DOM mode selector is changed
function modeChangedEvent() {
  curDrawMode = sel_mode.value();
}

// Function called when "show data" button is pressed.
// Presents a single JSON object in the HTML.
function buttonPressedEvent() {
  var obj = letterParams[debugLetter]
  var json = JSON.stringify(obj, null, 2);
  var text = select('#output');
  text.html(json)
}

// global front and back colors
var colorFront = [207, 222, 227];
var colorBack = [29, 42, 46];

// draws one of the three rectangles of a character
function drawPart(y_offset, pos, tilt) {
  push();
  translate(pos, y_offset);
  rotate(tilt);

  var scale = 10;

  fill(colorFront);
  rect(-20*scale, -3*scale, 20*scale, 3*scale);
  pop();
}

// draws a single character given an object, position, and scale
function drawFromDataObject(x, y, s, obj) {
  push();
  translate(x, y);
  scale(s, s);
  drawPart(-50, obj["box1"]["position"], obj["box1"]["tilt"]);
  drawPart(  0, obj["box2"]["position"], obj["box2"]["tilt"]);
  drawPart( 50, obj["box3"]["position"], obj["box3"]["tilt"]);
  pop();
}

function computeCurrentSoloChar() {
  // now figure out what object to draw
  var obj;
  if (soloIsAnimating) {
    nextObj = letterParams[soloCurLetter];
    // interpolation logic here
    obj = {};
    obj["box1"] = {};
    obj["box1"]["position"] = map(soloCurAnimationFrame, 0, soloNumAnimationFrames, soloPrevObj["box1"]["position"], nextObj["box1"]["position"])
    obj["box1"]["tilt"] = map(soloCurAnimationFrame, 0, soloNumAnimationFrames, soloPrevObj["box1"]["tilt"], nextObj["box1"]["tilt"])
    obj["box2"] = {};
    obj["box2"]["position"] = map(soloCurAnimationFrame, 0, soloNumAnimationFrames, soloPrevObj["box2"]["position"], nextObj["box2"]["position"])
    obj["box2"]["tilt"] = map(soloCurAnimationFrame, 0, soloNumAnimationFrames, soloPrevObj["box2"]["tilt"], nextObj["box2"]["tilt"])
    obj["box3"] = {};
    obj["box3"]["position"] = map(soloCurAnimationFrame, 0, soloNumAnimationFrames, soloPrevObj["box3"]["position"], nextObj["box3"]["position"])
    obj["box3"]["tilt"] = map(soloCurAnimationFrame, 0, soloNumAnimationFrames, soloPrevObj["box3"]["tilt"], nextObj["box3"]["tilt"])
  }
  else {
    obj = letterParams[soloCurLetter];
  }
  return obj;
}

function computeCurrentChosenChar(n) {
  // now figure out what object to draw
  var obj;
  if (chosenIsAnimating[n]) {
    if(chosenCurAnimationFrame[n] < 0) {
      obj = chosenPrevObjs[n];
    }
    else {
      nextObj = letterParams[chosenLetters[n]];
      // interpolation logic here
      obj = {};
      obj["box1"] = {};
      obj["box1"]["position"] = map(chosenCurAnimationFrame[n], 0, chosenNumAnimationFrames, chosenPrevObjs[n]["box1"]["position"], nextObj["box1"]["position"])
      obj["box1"]["tilt"] = map(chosenCurAnimationFrame[n], 0, chosenNumAnimationFrames, chosenPrevObjs[n]["box1"]["tilt"], nextObj["box1"]["tilt"])
      obj["box2"] = {};
      obj["box2"]["position"] = map(chosenCurAnimationFrame[n], 0, chosenNumAnimationFrames, chosenPrevObjs[n]["box2"]["position"], nextObj["box2"]["position"])
      obj["box2"]["tilt"] = map(chosenCurAnimationFrame[n], 0, chosenNumAnimationFrames, chosenPrevObjs[n]["box2"]["tilt"], nextObj["box2"]["tilt"])
      obj["box3"] = {};
      obj["box3"]["position"] = map(chosenCurAnimationFrame[n], 0, chosenNumAnimationFrames, chosenPrevObjs[n]["box3"]["position"], nextObj["box3"]["position"])
      obj["box3"]["tilt"] = map(chosenCurAnimationFrame[n], 0, chosenNumAnimationFrames, chosenPrevObjs[n]["box3"]["tilt"], nextObj["box3"]["tilt"])
    }
  }
  else {
    obj = letterParams[chosenLetters[n]];
  }
  return obj;
}

// var lastKeyPressedTime;
// var secondsUntilSwapMode = 30;
// var lastWordSwappedTime;
// var isSwappingWords = true;
// var secondsPerWord = 5;
// var curSwapWord = 0;
// var swapWords = [

function draw () {
  // update debug letter from sliders
  if(curDrawMode != "exhibit") {
    letterParams[debugLetter] = sliderToDataObject();
  }

  now = millis();
  // check to see if we should go into swapping mode
  if(!isSwappingWords && lastKeyPressedTime + 1000 * secondsUntilSwapMode < now) {
    isSwappingWords = true;
  }

  if(isSwappingWords) {
    if(lastWordSwappedTime + 1000 * secondsPerWord < now) {
      lastWordSwappedTime = now;
      curSwapWord = (curSwapWord + 1) % swapWords.length;
      for(var i=0; i<8; i++) {
        var c = swapWords[curSwapWord][i];
        swapExhibitLetter(i, c, 6*i);
      }
    }
  }

  background(colorBack);
  fill(colorFront);
  stroke(95, 52, 8);

  // shorthand variables to allow margin
  var o = 40
  var w2 = width - 2 * o
  var h2 = height - 2 * o
  if (curDrawMode == "alphabet") {
    var cur_index = 0;
    var hw = (w2 / 9.0) / 2.0;
    var hh = (h2 / 4.0) / 2.0;
    for(var j=0; j<4; j++) {
      for(var i=0; i<9; i++) {
        var cur_letter = letters[cur_index];
        var obj = letterParams[cur_letter];
        drawFromDataObject(o + hw + i * w2/9.0, o + hh + j * h2/4.0, 0.20, obj)
        cur_index = cur_index + 1;
      }
    }
  }
  else if (curDrawMode == "solo") {
    // see if animation should be turned off
    if(soloIsAnimating && soloCurAnimationFrame >= soloNumAnimationFrames) {
      soloIsAnimating = false;
    }
    // if we are animating, increment the number of animation frames
    if(soloIsAnimating) {
      soloCurAnimationFrame = soloCurAnimationFrame + 1;
    }
    var obj = computeCurrentSoloChar();
    drawFromDataObject(o + w2/2.0, o + h2/2.0, 1.0, obj)
  }
  else if (curDrawMode == "exhibit") {
    for(var i=0; i<8; i++) {
      // see if animation should be turned off
      if(chosenIsAnimating[i] && chosenCurAnimationFrame[i] >= chosenNumAnimationFrames) {
        chosenIsAnimating[i] = false;
      }
      // if we are animating, increment the number of animation frames
      if(chosenIsAnimating[i]) {
        chosenCurAnimationFrame[i] = chosenCurAnimationFrame[i] + 1;
      }
      var obj = computeCurrentChosenChar(i);
      drawFromDataObject(o + w2/10.0 + i*w2/8.0, o + h2/2.0, 0.25, obj)
    }
  }
}

function swapExhibitLetter(n, c, frameDelay) {
  chosenPrevObjs[n] = computeCurrentChosenChar(n);
  chosenLetters[n] = c;
  chosenIsAnimating[n] = true;
  chosenCurAnimationFrame[n] = 0 - frameDelay;
}

function keyTyped() {
  if (key == '!') {
    saveBlocksImages();
  }
  else if (key == '@') {
    saveBlocksImages(true);
  }
  else {
    lastKeyPressedTime = millis();
    if(isSwappingWords) {
      isSwappingWords = false;
    }
    upper_key = key.toUpperCase();
    if (upper_key in letterParams) {
      if(curDrawMode == "solo") {
        soloPrevObj = computeCurrentSoloChar();
        soloCurLetter = upper_key;
        soloIsAnimating = true;
        soloCurAnimationFrame = 0;        
      }
      else if(curDrawMode == "alphabet") {
        sel_char.value(upper_key);
        dataObjectToSliders(letterParams[upper_key]);
      }
      else if(curDrawMode == "exhibit") {
        swapExhibitLetter(curChosenLetter, upper_key, 0);
        curChosenLetter = (curChosenLetter + 1) % 8;  
      }
    }
  }
}