Lucid Projects

Blog - Day to day mumblings...
P5js Sound Collaboration

P5js Sound Collaboration

20 Nov 2019 - Jake Sherwood

Exploring music collaboration with p5js Exploring music collaboration with p5js

Exploring music collaboration with p5js

The assignment was to manipulate sound with p5js with a partner and present to the class.

I thought it would be cool to figure out a way to collaborate live with my partner for our presentation.

This meant I needed to figure out how to get 2 sketches to talk to each other.

Following Dan Shiffmans’s tutorial here I was able to get a node.js server set up and have two instances of a p5 sketch talk to each other over a local network.

This allowed Na and I to collaboratively sample audio tracks and manipulate them in realtime.

I have a serve_sound.js file that acts as my node server.

It is listening from transmitions from one instance of the sketch and stores them to a local directory. It then notifies the other instance that there is a new sample available.

server_sound.js code:

const express = require('express'); //make express available
const app = express(); //invoke express
const multer  = require('multer') //use multer to upload blob data
const upload = multer(); // set multer to be the upload variable (just like express, see above ( include it, then use it/set it up))
const fs = require('fs'); //use the file system so we can save files

app.use(express.static('public'));'/upload', upload.single('soundBlob'), function (req, res, next) {
  console.log(req.file); // see what got uploaded

  let uploadLocation = __dirname + '/public/uploads/' + req.file.originalname // where to save the file to. make sure the incoming name has a .wav extension

  fs.writeFileSync(uploadLocation, Buffer.from(new Uint8Array(req.file.buffer))); // write the blob to the server as a file
  res.sendStatus(200); //send back that everything went ok


//makes the app listen for requests on port 3000
let server = app.listen(3000, function(){
  console.log("app listening on port 3000!")

console.log("my socket server is running");

let socket = require('');

let io = socket(server);

io.sockets.on('connection', newConnection);

function newConnection(socket){
	console.log('new connection' +;
	//socket.on('mouse', mouseMsg);
	socket.on('oneSound', simpleSound);
	function simpleSound(myName){
		socket.broadcast.emit('oneSound', myName);
		console.log("simpleSound ran");


The sketch_sound_new.js file controls the recording, playback and looping. We were able to run 2 instances of the same sketch on a local network and have them communicate via


let oscs = [];
let mic;
let recorder;
let loopSound;
let loops = [];
let serverSound;
let loopCounter = 0;

let soundBlob;
let myName; // used for naming soundfiles on server
let startName = 0; // starting soundfile name

let state = 0; // state of recording mic sample

let doorSound;
let beatTrack;
let piano;
let sax;

let notes = [1, 1.125, 1.25, 1.334, 1.5, 1.667, 1.875, 2];

let socket;

let rectColor = 'green';
let loopText = 'Press L to record loop sample from mic.';

let formdata; //create a from to of data to upload to the server

function preload() {
//  doorSound = loadSound('doorchime.mp3');
  beatTrack = loadSound('drums.wav');
  piano = loadSound('piano3.wav');
  sax = loadSound('sax.wav');

function setup() {
  createCanvas(400, 400);

  // set up connection to local host server
  //socket = io.connect(''); // need to up date IP to share with another user on same network - or localhost:3000
   socket = io.connect('localhost:3000/');  // need to up date IP to share with another user on same network -  - or localhost:3000
  //socket = io.connect(''); //jakes home network IP
  // set up receiver from local host server
  //socket.on('mouse', newDrawing);
  socket.on('oneSound', instanceSound);

  // Set up mic
  mic = new p5.AudioIn();
  // Set up recorder
  recorder = new p5.SoundRecorder();
  // Connect mic to recorder
  // Turn on mic



function instanceSound(myName) {
  //play sound here when oneSound broadcast is received;
  console.log("new loop received");
  loops.push(loadSound('uploads/' + myName + '.mp3'));



function draw() {
  // Display instructions
  textAlign(CENTER, CENTER);
  rect(width / 2 - 140, 70, 20, 20);

  let ins = [loopText, 'Press Z to stop and remove last loop sample',  'Press B to start or pause the beat', 'Press P to play or stop piano' ,'Press S to send loop & play sax','Press O to change sample rate', 'Press D to add delay to sample', 'Press R to add reverb to sample'];

  for (let i = 0; i < ins.length; i++) {
    text(ins[i], width / 2, i * 30 + 70)


function keyPressed() {
  //fix Failed to execute 'createBuffer' on 'BaseAudioContext error	
  if (key === 'p') {
    // let f = random(notes);
    let f = notes[2];
    // piano.loop();
    if (piano.isPlaying()) {
    } else {
    console.log('p pressed - start piano');


  if (key === 'o') {
    // change rate of last mic sample
    let speed = map(mouseY, 0.1, height, 0, 2);
    speed = constrain(speed, 0.01, 4);
    // let oscSample = loops[loopCounter];

  if (key === 'd'){
  	 delay = new p5.Delay();
  	 delay.process(serverSound, .12, .7, 2300);
  if (key === 'r'){
  	 reverb = new p5.Reverb();
  	 reverb.process(serverSound, 3,2);

  if (key === 'l') {
    console.log("l pressed");
    // Increment the state on every keypress
    // There are only 3 states
    state %= 3;

    // Record the sample in state 1
    if (state == 1) {
      loopSound = new p5.SoundFile();
      loopText = 'Press L to stop recording loop sample';
      rectColor = 'red';

      // Stop recording in state 2
    } else if (state == 2) {
      loopText = 'Press L to play recorded loop sample';
      rectColor = 'gray';

      soundBlob = loopSound.getBlob();

      // Loop the sample in state 3
    } else {
      loopText = 'Press L key to record loop sample';
      rectColor = 'green';


  if (key === 'z') {
    console.log('z pressed - stop or replay loop');
    let lastLoop = loopCounter - 1;
    console.log('stopped loop' + lastLoop);

  if (key === 'n') {

    console.log('n pressed - play new loop');
    serverSound = loops[loopCounter];
    console.log("I'm playing loops " + loopCounter)

  // start beat track
  if (key === 'b') {
    if (beatTrack.isPlaying()) {
    } else {
    console.log('b pressed - start beat');
  // stop beat track
  if (key === 'x') {
    console.log('x pressed - stop beat');
  // send loop
  if (key === 's') {
    console.log('s pressed - send loop');
    socket.emit('oneSound', myName); // name is important 
    console.log("sent loop");

  return false; // prevent default

function sendSoundBlob() {

  if (startName == 0) {
    myName = startName;

  console.log("startName " + startName);
  myName = startName;

  //need to create new formdata object everytime
  formdata = new FormData(); //create a form of data to upload to the server

  formdata.append('soundBlob', soundBlob, myName + '.mp3'); // append the sound blob and the name of the file. third argument will show up on the server as req.file.originalname

  // Now we can send the blob to a server...
  var serverUrl = '/upload'; //we've made a POST endpoint on the server at /upload
  //build a HTTP POST request
  var httpRequestOptions = {
    method: 'POST',
    body: formdata, // with our form data packaged above
    headers: new Headers({
      'enctype': 'multipart/form-data' // the enctype is important to work with multer on the server

  // console.log(httpRequestOptions);
  // console.log(formdata);

  // use p5 to make the POST request at our URL and with our options
    (successStatusCode) => { //if we were successful...
      console.log("uploaded recording successfully: " + successStatusCode)
    (error) => {



Overall I had a lot of fun with this assignment. If had it to do over I would add a few more features and a way of manipulating the tracks individually instead of only the last one.

categories: icm

join me on this crazy ride. enter your email:

contact [at] jakesherwood [dot] com

contact me if you'd like to work together