Speed reading using Spritz, Spreeder, RSVP

There’s a technique that enables faster reading called RSVP – Rapid Serial Visual Presentation.  This was developed initially for human research trials, enabling researchers to very accurately measure response time based on visual cognition.

A number of people have leveraged this into speed reading applications and services.  The latest of which is Spritz.  Most of these services prior to Spritz showed each word for an equal length of time, and centered the word in the viewing area. Spritz appears to be easier to read, however, and there are a few key things they do differently:

Word offset

Each word displayed is offset.  They’ve determined what they call the Optimal Recognition Point (ORP) for each word, and that point on each word remains in the same position in the window.  It turns out that this point is derived from the length of the word only.  One letter words are shown with that letter in the ORP position. Words 2-5 letters long are shown with the second letter in that position.  Words with 6-9 letters are shown with the third letter in that position.  Words between 10-13 letters are shown with the fourth letter in that position.  The window isn’t meant to handle words longer than 13 letters, and Sprint claims that longer words are difficult to read in this method, so they typically split them up using typical hyphenation rules.

Word display time

Each word is displayed for a different length of time.  Unfortunately this isn’t calculated on the fly with their javascript software, so it can’t be readily determined how they are doing this.  Their website has static arrays containing a position/flag value, a time multiplier, and the word to display.  Their software simply runs through this array during reading.  There is a word reading ratio, 1.21, which they use to increase the word speed, so a reading speed of 250 results in wordtime=60,000/(250*1.21)=198mS per word.  However this is then multiplied by the word’s multiplier.  A multiplier of 21 would result in about 250WPM, so any multipliers smaller than 21 are faster than the stated WPM, and multipliers smaller are slower. The actual display time is (1+multiplier/100)*wordtime.

A few patterns I’ve noticed, though each of these appear to have many exceptions in the script I analyzed:

  • Words longer than 7 characters are given a multiplier of 30.
  • At the beginning of sentences long words are often given multipliers of 140.
  • The first time a long word is encountered in a text it is given a value of 140.
  • Popular words and words of middling length are given values of 0.
  • The first time a word of any length is encountered it is sometimes given a higher value – 110 is common for 6 letter words.
  • Very few 5 letter words are given anything other than a 0.
  • Some shorter words are given longer times for no apparent reason – maybe they are less used english words, or the sentence structure suggests a pause (comma, semicolon)? More analysis needed here.
  • At the beginning of sentences shorter words aren’t lengthened like long words are.
  • 3 and 2 letter words, like long words, are given a multiplier of 30.
  • End of sentences inside a paragraph are given a 110 pause (no word displayed)
  • End of paragraphs are given a 220 pause (no word displayed)
  • End of sections are given a 330 pause (no word displayed)

It seems like it would be a fun project for a small display and battery – reading books on your wrist.

RepeatMe! game for Arduino

This is example code for a game I developed based loosely on Simon Says and similar electronic games.  It uses an Arduino, four buttons, four LEDs, and a speaker, and is essentially a sequence memorization game.  It will play a note and light an LED, after which the user presses the button associated with that note and LED in order to repeat the game’s sequence.  The game adds another step so the sequence is two notes/LEDs long, and the user repeats that two step sequence.  This continues, the game adding another step to the sequence, the user repeating the sequence, until either the user makes a mistake, or the user successfully plays the game to the point where the sequence is 30 steps long.

Simply cut and paste into your Arduino IDE to use.

/*
  RepeatMe!
  
  Author:     Adam Davis 
  Contact:    adavis@ubasics.com
  Date:       2 June 2011
  
  SETUP
  
  This project requires one Arduino, four buttons, four LEDs, and one 
  speaker.
  
  The LEDs are connected to pins 3, 9, 10, 11 and VCC (with a resistor)
  The buttons are connected to pins 5, 6, 7, 8 and ground
  The speaker is connected to pin 4 and ground
  
  PLAY
  
  When started, the game blinks and sings randomly.  Any button press will 
  enter game mode.
  
  In game mode the game simultaneously lights an LED and plays a note.
  The player presses the button that is associated with that light and
  note, then the game plays the note and lights the LED again, and then 
  another light and note in a two note/light sequence.  The player then 
  copies the sequence by pressing the two buttons associated with the 
  lights and notes, and then the game adds another light/note to the 
  sequence which the player must again copy.
  
  Game play continues with the game adding new steps to the sequence until
  either the sequence is 30 steps long, or the player makes a mistake in 
  copying the sequence.
  
  If the player succeeds in completing the full 30 step sequence, all LEDs
  are lit briefly, then the game resets.
  
  If the player makes a mistake, the game plays a low note, then resets.
  
  When connected to the computer with a terminal at 9600bps game information
  is displayed as the game is played.
  
  IMPROVEMENTS

   - Make a winning song for the game to play on winning
   - Make losing more interesting - perhaps it gives you a second and third chance
   - Make it explicitly four player - each player gets one button, they 
     have to play them in proper sequence, and when one person gets it 
     wrong they are out, however the game plays their part so the others
     can continue play to find the winner - the last one remaining
   - Add more game modes
   - Make the sequence playback variable speed - as the sequence gets longer,
     each step is played for a shorter time
   - Remove the 30 sequence limit and see how high people can get.  Keep a
     maximum score to encourage people to beat the highest successful sequence
   - Add some achievements or badges (Use serial port and computer software, or
     LCD support to view and post about achievements on social networks)
   - The LEDs are on PWM capable pins - use PWM to pulse them in a more pleasing
     manner than the existing hard on and off
   - The attraction mode the game starts up in could play more interesting
     melodies and show more interesting LED patterns
   - Could be used as a 4 key piano, or even record and play back the user's song
   - Complex audio generation using a wavetable or sinewaves could produce
     fun synthesizer or more interesting sounds.  Add a microphone and play
     the game with a recorded sound
  
  LICENSE
  
  This code is copyright 2011 Adam Davis, and is provided to the public as
  a public domain work.  You may use it in any way or for any purpose you
  desire.  If it makes things easier for you, feel free to consider this
  work licensed under the BSD, Apache, MIT, or GNU license of your choice. 
  See http://opensource.org/licenses/category for more information on these
  licenses.
  
  This code is provided AS IS without warranties or conditions of any kind.
  You are solely responsible for determining the appropriateness of use or
  redistribution of this code or any derivative, and assume any risks.
 */

// Pin definitions
const static int led[4] = {3, 9, 10, 11};
const static int button[4] = {5, 6, 7, 8};
const static int spk = 4;
const static int pentatonic[5] = {262, 294, 330, 392, 440}; // C, D, E, A, G

int buttonpress;

// Game states
enum GameStates{START, ADD, REPEAT, QUIZ, WIN, LOSE};
int state;
int memory[31];
int count;

// Button and LED macros
#define btn(x) (!(digitalRead((button[(x)]))))
//#define ledpwm(x,y) (analogWrite((led[(x)]),(255-(y))))
#define led(x,y) (digitalWrite((led[(x)]),(!(y))))

int debounce(void);
int leds(int ledvalue);

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  Serial.begin(9600);
  pinMode(13, OUTPUT);     
  for(int index=0; index<4; index++)
  {
    pinMode(led[index], OUTPUT);
    digitalWrite(led[index], HIGH);
    pinMode(button[index], INPUT);
    digitalWrite(button[index], HIGH); // Turn on the pull up resistors
  }
  pinMode(spk, OUTPUT);
  randomSeed(analogRead(0));
  state = START;
  Serial.print("hi");
}

void loop() {
  switch(state)
  {
    case START: // Attract mode
      leds(random(16));
      tone(spk, pentatonic[random(5)]);
      delay(100);
      if(debounce() > -1)
      {
        noTone(spk);
        leds(0);
        Serial.print(" Waiting for you to let go of button\n");
        while(debounce() >= 0);
        delay(500);
        count = 0;
        state = ADD;
      }
      break;
    case ADD: // Add a new step to the existing sequence
      Serial.print("Add\n");
      memory[count] = random(4);
      count++;
      state = REPEAT;
      break;
    case REPEAT: // Play the current sequence
      Serial.print("Repeat\n");
      for(int index=0; index < count; index++)
      {
        tone(spk, pentatonic[memory[index]], 400);
        leds(1<<memory[index]);
        delay(400);
        leds(0);
        delay(100);
      }
      state = QUIZ;
      break;
    case QUIZ: // Quiz the user on the current sequence
      Serial.print("Start quiz\n");
      int answer;
      state = ADD; // Default go to ADD
      for(int index=0; index < count; index++)
      {
        Serial.print(" Wait for button\n");
        while((answer = debounce()) < 0); // Wait for a button press
        // Light the LED
        led(answer, 1);
        Serial.print(" Got button\n");
        // If it's the right button play the tone, if not go to lose
        if(answer == memory[index])
        {
          // Correct
          Serial.print(" Correct\n");
          tone(spk, pentatonic[answer]);
          if(count >=31)
          {
            state = WIN;
          }
        }
        else
        {
          // Incorrect
          Serial.print(" Incorrect\n");
          state = LOSE;
          tone(spk, 60);
          leds(15);
          Serial.print(" Waiting for you to let go of button\n");
          while(debounce() >= 0);
          break; // Stop the for loop before it finishes
        }
        // wait for button to stop, then go to next tone
        Serial.print(" Waiting for you to let go of button.\n"); 
        while(debounce() >= 0);
        leds(0);
        noTone(spk);
      }
      delay(500);
      break;
    case WIN: // User won
      Serial.print("Win\n");
      leds(15);
      delay(250);
      leds(0);
      delay(250);
      break;
    case LOSE: // User lost
      Serial.print("Lose\n");
      Serial.print(" Waiting for a new button press\n");
      if(debounce() >= 0)
      {
        state = START;
      }
      break;
    default: // Uh oh, something bad happened, reset game
      Serial.print("Default\n");
      state = START;
      break;
  }
}

// Check all four buttons. If a button is pressed, wait 30 mS then check again.
// If it's still pressed, return that button number as a successful button press.
int debounce(void)
{
  int index;
  for(index=0; index<4; index++)
  {
    if(btn(index))
    {
      delay(30);
      if(btn(index))
      {
        return index;
      }
    }
  }
  return -1;
}

// Light the LEDs according to the first four bits in ledvalue.
int leds(int ledvalue)
{
  
  led(0, ledvalue & 1);
  led(1, ledvalue & 2);
  led(2, ledvalue & 4);
  led(3, ledvalue & 8);
}

ELPH Begins…

About a year ago I met Dante Kube at the Detroit Maker Faire.  He explained that he was creating a replica of the Daft Punk Discovery Era helmets, and was in need of some help putting the electronics together.

The electronics package for the Guy Man helmet (right side of above picture) includes 5 areas.  The right and left sides are mirror images of each other, so there are three different types of LED assemblies that need to be built:

- The central “face” portion is an array of LEDs, 36 tall by 14 wide, 504 total LEDs
- The side color bars consist of 8 large regions of color (one for the right side, one for the left side)
- The chin portions consist of one bar of 8 white LEDs, horizontally in one row above an array, 6 tall by 8 wide, of red, yellow, and green LEDs.  This lower array has the appearance of 6 bar graphs, using rectangular LEDs. (one for the right chin, one for the left chin)

Dante does not want to implement the central face portion at this time, this project will therefore focus on the chin and color bar assemblies.

Having performed a few tests on a simple scanned LED array, I’m not happy with the brightness and CPU usage required to drive it well.  In addition, scanned arrays suffer problems with photography and video recording, as the scanning appears to flicker in the video, and photographs may show portions darker due to timing issues.  They look fine to people viewing them in person, but I did not want to compromise the effect merely because someone wanted to post a video of the helmet to youtube.

This can be fixed by using a non-scanned array, where each LED is controlled by a single I/O line, rather than sharing lines with other LEDs as with a scanned array.  Recently, due largely to advanced LED backlights in televisions, many companies have designed LED drivers that provide a great deal of control over each LED, giving each one their own I/O pin, PWM control, and a real current driver.

One of these chips is the Texas Instruments TLC59401 which can drive 16 LEDs.  The chin board and side board, together, comprise 59 LEDs, which would require 4 of these chips.  Further, the control lines are combined so that only a few control lines need to go to the main microcontroller, rather than an array of wires.  This also decreases assembly time as we don’t need to include additional driver transisters, resistors, and other support components that an array would require.  Current control is built in to these chips.

While I expect the layout to change, the general design for each chin board will include 4 of these chips, and the chin LEDs mounted directly to the board.  The sidebar LEDs will be connected to this board so those LEDs will not require separate chips.  There are a few LEDs that won’t be used on the chin board – these will be available for the pulsing LEDs in the earpieces.

Here’s a quick mockup of the layout.  The board size isn’t relevant for this first test version, the primary question is whether the LED placement fits the helmet, and whether the LED control lines from the four chips can get to the LEDs.

For reference, the grid is 0.1 inches between dots.  The outline size of the board is 3.8″ x 2.5″.

So far I have not been able to find superbright rectangular LEDs.  The design will probably use superbright round LEDs for now, and if suitable rectangular LEDs are located they can be used in the final design.  Alternately, we can grind down the round LEDs to create rectangular LEDs.

I’m targeting using a PIC32 microcontroller as the brains of the electronics package.  It’s inexpensive, yet has a lot of power, memory, and capability.  While the initial animations may be pretty simple, later programming could implement very interesting and complex features.  Given that the whole LED package needs only a few control lines, it could easily be controller by an Arduino or similar device as well.  A connector will be chosen so that the microcontroller can be changed out, and one could use a variety of controllers for the helmet, including MIDI or computer controllers.

Simon Springs

Last year at the Detroit Maker Faire, I was chatting with someone from a budding hacker space about how to get people excited about making.  We came to the conclusion that one of the nice aspects of a good maker space was being able to walk in with nothing, and walk out a few hours later with a fun game, toy, or project that kids and adults would enjoy playing with.

On the spur of the moment I thought that having a Simon Says type electronic game, based on the Arduino, would be easy to create, and that since pressure sensitive pads were easily and cheaply available (ie, Harbor Freight’s alarm pads for about $11) then extending the Simon Says game to require whole body movement would be easy.  Further, it would become social if it was a cooperative game that required 4 people to work together to memorize and play back the melody – each person assigned to one pad, and they have to pay attention and jump in order.

I hadn’t acted on that brainstorming session, but now that I need a project for the Ann Arbor Mini Maker Faire, I decided to go ahead and do this.

So I have the pressure pads, and will code up a test game soon for this.  Taking one pressure pad apart, it was as simple as I expected, and I think I should be able to instruct people on how to make their own with a freezer ziplock bag as the encasement, some paper, sheets of aluminum foil, and foam.  The Arduino will be the most expensive part, and using the bare bones Arduinos, one could probably keep the total cost of the kit to below $20.

Update:

A successful make and take project was created from this effort, and the game code is now available.

Beginning transmission x9.38348.752.9163

Working on what will likely be my first iPhone app.  It’s embarrassing, really, but you have to publish something as your first thing, and if you allow creeping featurism to delay the release you might never release it.

There’s always something that can be improved.

For the same reason, I’ve decided to skip supporting retina or iPad at the moment.  If the app has some success and interest, then I’ll pursue those opportunities.