The Space Replay

A hovering object that explores and manipulates transitional public spaces with particular acoustic properties. By constantly recording and replaying these ambient sounds, the levitating sphere produces a delayed echo of human activity.

In this project, the Arduino board and an Adafruit Wave Shield is used. The Wave shield is not designed to record audio however, they say that they use the board for both recording and playback.

The sphere responds sonically to people and its surroundings by means of a battery-powered Arduino, an Adafruit Wave Shield hacked to record and playback audio on-the-fly and a small speaker.

This generates a few questions.

  • How would this be done?
  • Where would the microphone be placed inside the balloon for decent audio capture?
  • Would there be a better way to design this?
  • No you can't use the board for recording. You can however use the Arduino's own ADC to record sound.
    – jippie
    Mar 8 '14 at 13:37

It seems to be possible to record with the shield using a library called waverp

WaveRP is an Arduino library for recording and playing Wave files with the Adafruit Wave Shield. It records 8-bit mono files at 4,000 to 44,100 samples per second.

Some details adafruit blog:

It records 8-bit mono files at 4,000 to 44,100 samples per second.


As jippie said, you can use the Arduinos ADC to record audio, and presumably save that to the Arduino's RAM (the SD card is probably too slow for directly saving it). Then it writes the buffer to the SD card.

  • 1
    But an Arduino has 2K of RAM, so even at low quality sampling rates like 8kHz 8 bit samples, that would cover only about 250ms. Mar 8 '14 at 22:54

Maybe is kind of late but I found the code for the Space Replay is in here at the bottom of the last image, specifically in here


        __-__         Code for Space Replay, a floating black orb that records
      .X+..   ·       and plays back ambient sounds. This sketch was adapted by
    .Xx+-·..    ·     Francesco Tacchini, with the help of Oliver Smith & Brock
    XXx++-...         Craft, from the original WaveRecordPlay example sketch
    XXxx++--.....·    written by Bill Greiman. 
    `XXXxxx++---'     This project was created as part of a university project at
      `XXXxxxx'       the Royal College of Art. It's a collaboration with Julinka
         """          Ebhardt and Will Yates-Johnson. See more: vimeo.com/88992597


#include <SdFat.h>
#include <WaveRP.h>
#include <SdFatUtil.h>
#include <ctype.h>

#define MIC_ANALOG_PIN 0             // Define the analog pin
#define RECORD_RATE 44100            // Define the record rate
#define PRINT_FILE_INFO 0            // Useful for debug: print file info and
#define PRINT_DEBUG_INFO 1           // bad wave file size or SD busy errors
#define MAX_FILE_SIZE 104857600UL    // 100MB or 150 min at 11025 samples/second

// debugging led
int playLED = 7;
int recLED = 8;
int delStopLED = 9;

// global variables
Sd2Card card;           // SD/SDHC card with support for version 2.00 features
SdVolume vol;           // FAT16 or FAT32 volume
SdFile root;            // volume's root directory
SdFile file;            // current file
WaveRP wave;            // wave file recorder/player
int16_t lastTrack = -1; // Highest track number
uint8_t trackList[32];  // bit list of used tracks

// print error message and halt
void error(char* str) {
  PgmPrint("error: ");
  if (card.errorCode()) {
    PgmPrint("sdError: ");
    Serial.println(card.errorCode(), HEX);
    PgmPrint("sdData: ");
    Serial.println(card.errorData(), HEX);
uint8_t trackName(int16_t number, char* name) {
  if (0 <= number && number <= 255) {
    strcpy_P(name, PSTR("TRACK000.WAV"));
    name[5] = '0' + number/100;
    name[6] = '0' + (number/10)%10;
    name[7] = '0' + number%10;
    return true;
  PgmPrint("Invalid track number: ");
  return false;
// start file playing
uint8_t playBegin(char* name) {
  if (!file.open(&root, name, O_READ)) {
    PgmPrint("Can't open: ");
    return false;
  if (!wave.play(&file)) {
    PgmPrint("Can't play: ");
    return false;
  Serial.print(wave.bitsPerSample, DEC);
  PgmPrint("-bit, ");
  PgmPrintln(" kps");
  if (wave.sdEndPosition > file.fileSize()) {
    PgmPrint("play Size mismatch,");
  PgmPrint("Playing: ");
  return true;
// play all files in the root dir
void playTracks(void) {
  dir_t dir;
  char name[13];
  uint8_t np = 0;
  while (root.readDir(&dir) == sizeof(dir)) {
    //only play wave files
    if (strncmp_P((char *)&dir.name[8], PSTR("WAV"), 3)) continue;
    // remember current dir position
    uint32_t pos = root.curPosition();
    // format file name
    SdFile::dirName(dir, name);
    if (!playBegin(name)) continue;
    while (wave.isPlaying()) {
      uint8_t c = Serial.read();
// delete a track
void trackDelete(int16_t track) {
  char name[13];
  if (!trackName(track, name)) return;

  if (SdFile::remove(&root, name)) {
  else {
    PgmPrintln("Delete failed!");
// Finally we have the normal SETUP and LOOP functions -------------------------
void setup(void) {
  if (!card.init()) error("card.init");
  if (!vol.init(&card)) error("vol.init");
  if (!root.openRoot(&vol)) error("openRoot");

  pinMode(playLED, OUTPUT);
  pinMode(recLED, OUTPUT);
  pinMode(delStopLED, OUTPUT);

// loop to play and record files.
void loop() {
  digitalWrite(delStopLED, HIGH);
  if (file.isOpen()){ 
    file.close();      // Make sure file is closed

  int16_t track = -1;
  uint8_t c; 
  if (track < 0 ) {
    track = lastTrack;

  char name[13];
  if (track < 0) track = lastTrack + 1;
  if (!trackName(track , name)) return;
  if (file.open(&root, name, O_READ)) {
  digitalWrite(delStopLED, LOW);
  digitalWrite(recLED, HIGH);
  Serial.println( "Recording" );    // Record new file...

  if (!file.createContiguous(&root, name, MAX_FILE_SIZE)) {
      PgmPrintln("Create failed");
      return; }
  if(!wave.record(&file, RECORD_RATE, MIC_ANALOG_PIN, ADC_REFERENCE)) {
      PgmPrintln("Record failed");
      return; }

  delay(5000);     // ...for x milliseconds
  digitalWrite(recLED, LOW);
  file.close();     // Stop the recording, trim and close the file

  digitalWrite(playLED, HIGH);
  playTracks();     // Play the file
  digitalWrite(playLED, LOW);

About your questions,

  • In the latest WaveRP library there's a folder where it explains how to attach a microphone to the waveshield. And I'm guessing I don't know for sure, but I'd say they're using a contact mic, the contact mics are both, mics and speakers, some cheap walkies also use them. So I'm guessing that they're using only one?

  • Contact mics pick up vibration so, I think the plastic cone that they're using is for de contact mic to pick up the vibrations of the surroundings sounds.

  • A better way to design it? I think that it depends. In a way is an art piece. So, a better way to design it... is like to ask VanGogh a better way to paint sunflowers. But I think always the sound can be better. A way to improve it could be making a better amplifier circuit for reduce the noise or separating the mic and the speakers... But I don't know, I think it depends what you want to make and what you want to achieve. Because if you want it to float is better to have the leastest components. But if the sound is the priority I think it can be much more better using an amplifier, a better mic, etc.

Also, now in the Adafruit Wave Shield's page there is a project of realtime voice changer, that also record and playbacks. More information here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.