var EnvelopedOscillator = function(oscillatorType, envelopeParameter) {
this.envelopeParameter = envelopeParameter;
this.oscillatorType = oscillatorType;
switch (oscillatorType) {
this.oscillator = new p5.Oscillator();
this.oscillator = new p5.Noise();
this.oscillator.setType(oscillatorType);
this.envelope = new p5.Env();
this.envelope.setADSR(envelopeParameter.attackTime, envelopeParameter.decayTime, envelopeParameter.susPercent, envelopeParameter.releaseTime);
this.envelope.setRange(envelopeParameter.attackLevel, envelopeParameter.releaseLevel);
EnvelopedOscillator.prototype.play = function(startTime, sustainTime, frequency) {
if (frequency) this.oscillator.freq(frequency);
this.envelope.play(this.oscillator, startTime, sustainTime);
EnvelopedOscillator.prototype.start = function() {
EnvelopedOscillator.prototype.stop = function() {
EnvelopedOscillator.prototype.pan = function(value) {
this.oscillator.pan(value);
EnvelopedOscillator.prototype.connect = function(unit) {
this.connectedUnit = unit;
this.oscillator.disconnect();
this.oscillator.connect(unit);
var ParallelEnvelopedOscillatorSet = function(oscillatorType, envelopeParameter, capacity) {
this.envelopedOscillatorArray = [];
this.capacity = capacity;
for (var i = 0; i < this.capacity; i++) {
this.envelopedOscillatorArray.push(new EnvelopedOscillator(oscillatorType, envelopeParameter));
ParallelEnvelopedOscillatorSet.prototype.play = function(startTime, sustainTime, frequency) {
this.envelopedOscillatorArray[this.currentIndex].play(startTime, sustainTime, frequency);
if (this.currentIndex >= this.capacity) this.currentIndex = 0;
ParallelEnvelopedOscillatorSet.prototype.start = function() {
for (var i = 0, len = this.envelopedOscillatorArray.length; i < len; i++) {
this.envelopedOscillatorArray[i].start();
ParallelEnvelopedOscillatorSet.prototype.stop = function() {
for (var i = 0, len = this.envelopedOscillatorArray.length; i < len; i++) {
this.envelopedOscillatorArray[i].stop();
ParallelEnvelopedOscillatorSet.prototype.pan = function(value) {
for (var i = 0, len = this.envelopedOscillatorArray.length; i < len; i++) {
this.envelopedOscillatorArray[i].pan(value);
create: function(intervalMillisecond) {
var newObject = Object.create(Metronome.prototype);
newObject.intervalMillisecond = intervalMillisecond;
newObject.lastNoteTimeStamp = 0;
newObject.clickCount = 0;
var currentTimeStamp = millis();
if (currentTimeStamp >= this.lastNoteTimeStamp + this.intervalMillisecond) {
this.lastNoteTimeStamp += this.intervalMillisecond;
if (currentTimeStamp >= this.lastNoteTimeStamp + this.intervalMillisecond) this.lastNoteTimeStamp = currentTimeStamp;
var Track = function(trackParameter) {
this.oscillatorSet = new ParallelEnvelopedOscillatorSet(trackParameter.oscillatorType, trackParameter.envelopeParameter, 8);
if (trackParameter.pan) this.oscillatorSet.pan(trackParameter.pan);
this.startTime = trackParameter.startTime;
this.sustainTime = trackParameter.sustainTime;
this.frequency = trackParameter.frequency;
this.notePattern = trackParameter.notePattern;
this.isRandom = trackParameter.isRandom;
this.probability = trackParameter.probability;
Track.prototype.updateNotes = function(noteCount) {
for (var i = 0; i < floor(noteCount / 2); i++) {
if (random(1) < this.probability) this.noteArray.push(true);
else this.noteArray.push(false);
this.noteArray = concat(this.noteArray, this.noteArray);
for (var k = 0; k < noteCount; k++) {
this.noteArray.push(false);
var patternLength = this.notePattern.length;
for (var m = 0; m < noteCount; m++) {
if (this.notePattern[m % patternLength]) this.noteArray[m] = true;
Track.prototype.play = function(noteIndex) {
if (this.noteArray[noteIndex]) this.oscillatorSet.play(this.startTime, this.sustainTime, this.frequency);
Track.prototype.start = function() {
this.oscillatorSet.start();
create: function(track, noteVisualizerCreator, xPosition, yPosition) {
var newObject = Object.create(TrackVisualizer.prototype);
newObject.noteVisualizerCreator = noteVisualizerCreator;
newObject.xPosition = xPosition;
newObject.yPosition = yPosition;
newObject.intervalLength = width * 0.05;
translate(this.xPosition, this.yPosition);
for (var i = 0, len = this.noteVisualizerArray.length; i < len; i++) {
if (i == floor(len / 2)) {
translate(0, height * 0.5);
translate(this.intervalLength, 0);
this.noteVisualizerArray[i].display();
this.noteVisualizerArray = [];
for (var i = 0, len = this.track.noteArray.length; i < len; i++) {
this.noteVisualizerArray.push(this.noteVisualizerCreator.create());
for (var i = 0, len = this.track.noteArray.length; i < len; i++) {
this.noteVisualizerArray[i].isWaiting = this.track.noteArray[i];
receivePlayedNote: function(noteIndex) {
if (this.noteVisualizerArray[noteIndex].isWaiting) {
this.noteVisualizerArray[noteIndex].isWaiting = false;
this.noteVisualizerArray[noteIndex].isPlayed = true;
this.noteVisualizerArray[noteIndex].playedFrameCount = 0;
var AbstractNoteVisualizer = {
var newObject = Object.create(AbstractNoteVisualizer.prototype);
newObject.playedFrameCount = 0;
newObject.isWaiting = true;
newObject.isPlayed = false;
newObject.unitLength = width * 0.01;
if (this.isWaiting) this.displayBeforePlay();
if (this.isPlayed) this.displayAfterPlay();
displayBeforePlay: function() {},
displayAfterPlay: function() {},
getProgressRatio: function() {
return min(1, this.playedFrameCount / this.fadeFrameCount);
getFadeRatio: function() {
return 1 - this.getProgressRatio();
var SineNoteVisualizer = {
var newObject = Object.create(SineNoteVisualizer.prototype);
Object.assign(newObject, AbstractNoteVisualizer.create());
displayBeforePlay: function() {
var diameter = 1.5 * this.unitLength;
ellipse(0, 0, diameter, diameter);
displayAfterPlay: function() {
if (this.playedFrameCount >= this.fadeFrameCount) return;
var progressRatio = (-pow(this.getProgressRatio() - 1, 4) + 1);
var fadeRatio = this.getFadeRatio();
strokeWeight(this.unitLength * 0.5 * fadeRatio);
stroke(64, 255 * fadeRatio);
var diameter = (2 + 4 * progressRatio) * this.unitLength;
ellipse(0, 0, diameter, diameter);
fill(64, 255 * fadeRatio);
0, -1 * this.unitLength - 8 * this.unitLength * progressRatio,
0.5 * this.unitLength, 2 * this.unitLength * (1 + fadeRatio)
Object.setPrototypeOf(SineNoteVisualizer.prototype, AbstractNoteVisualizer.prototype);
var ShortWhiteNoiseNoteVisualizer = {
var newObject = Object.create(ShortWhiteNoiseNoteVisualizer.prototype);
Object.assign(newObject, AbstractNoteVisualizer.create());
displayBeforePlay: function() {
var halfSize = 1.5 * this.unitLength;
quad(halfSize, 0, 0, halfSize, -halfSize, 0, 0, -halfSize);
displayAfterPlay: function() {
if (this.playedFrameCount >= this.fadeFrameCount) return;
var progressRatio = (-pow(this.getProgressRatio() - 1, 4) + 1);
var fadeRatio = this.getFadeRatio();
stroke(64, 255 * fadeRatio);
var halfSize = 1.5 * this.unitLength;
var maxDisplacement = halfSize * sq(fadeRatio);
translate(3 * this.unitLength * progressRatio + random(-1, 1) * maxDisplacement, random(-1, 1) * maxDisplacement);
quad(halfSize, 0, 0, halfSize, -halfSize, 0, 0, -halfSize);
Object.setPrototypeOf(ShortWhiteNoiseNoteVisualizer.prototype, AbstractNoteVisualizer.prototype);
var LongWhiteNoiseNoteVisualizer = {
var newObject = Object.create(LongWhiteNoiseNoteVisualizer.prototype);
Object.assign(newObject, AbstractNoteVisualizer.create());
displayBeforePlay: function() {
var halfSize = 1.5 * this.unitLength;
var halfInterval = halfSize * 0.4;
line(-halfSize, -halfInterval, halfSize, -halfInterval);
line(-halfSize, +halfInterval, halfSize, +halfInterval);
displayAfterPlay: function() {
if (this.playedFrameCount >= this.fadeFrameCount) return;
var progressRatio = pow(this.getProgressRatio() - 1, 5) + 1;
var fadeRatio = this.getFadeRatio();
stroke(64, 255 * fadeRatio);
var halfSize = 1.5 * this.unitLength * (1 + 1.5 * progressRatio);
var halfInterval = 2.5 * this.unitLength * progressRatio;
line(-halfSize, -halfInterval, halfSize, -halfInterval);
line(-halfSize, +halfInterval, halfSize, +halfInterval);
for (var i = 0; i < 7; i++) {
var y = random(-0.9, 0.9) * halfInterval;
line(-halfSize, y, halfSize, y);
Object.setPrototypeOf(LongWhiteNoiseNoteVisualizer.prototype, AbstractNoteVisualizer.prototype);
var BrownNoiseNoteVisualizer = {
var newObject = Object.create(BrownNoiseNoteVisualizer.prototype);
Object.assign(newObject, AbstractNoteVisualizer.create());
displayBeforePlay: function() {
var shapeSize = 2.5 * this.unitLength;
rect(0, 0, shapeSize, shapeSize, shapeSize * 0.2);
displayAfterPlay: function() {
if (this.playedFrameCount >= this.fadeFrameCount) return;
var progressRatio = pow(this.getProgressRatio() - 1, 5) + 1;
var fadeRatio = this.getFadeRatio();
fill(64, 255 * fadeRatio);
var shapeSize = 2.5 * this.unitLength;
var maxDisplacement = 1.5 * this.unitLength * pow(fadeRatio, 4);
translate(random(-1, 1) * maxDisplacement, (1 - 3 * progressRatio) * this.unitLength + random(-1, 1) * maxDisplacement);
rotate(PI * progressRatio);
rect(0, 0, shapeSize, shapeSize, shapeSize * 0.2);
Object.setPrototypeOf(BrownNoiseNoteVisualizer.prototype, AbstractNoteVisualizer.prototype);
var TrackSystem = function(noteCount) {
this.noteCount = noteCount;
this.trackVisualizerArray = [];
TrackSystem.prototype.start = function() {
for (var i = 0, len = this.trackArray.length; i < len; i++) {
this.trackArray[i].start();
this.updateNotes(this.noteCount);
this.initializeVisualizers();
this.updateVisualizers();
TrackSystem.prototype.playNextNote = function() {
for (var i = 0, len = this.trackArray.length; i < len; i++) {
if (soundEnabled) this.trackArray[i].play(this.nextNoteIndex);
for (var k = 0, klen = this.trackVisualizerArray.length; k < klen; k++) {
this.trackVisualizerArray[k].receivePlayedNote(this.nextNoteIndex);
if (this.nextNoteIndex >= this.noteCount) {
this.updateNotes(this.noteCount);
this.updateVisualizers();
TrackSystem.prototype.updateNotes = function(noteCount) {
for (var i = 0, len = this.trackArray.length; i < len; i++) {
this.trackArray[i].updateNotes(noteCount);
TrackSystem.prototype.display = function() {
for (var i = 0, len = this.trackVisualizerArray.length; i < len; i++) {
this.trackVisualizerArray[i].display();
TrackSystem.prototype.updateVisualizers = function() {
for (var i = 0, len = this.trackVisualizerArray.length; i < len; i++) {
this.trackVisualizerArray[i].update();
TrackSystem.prototype.initializeVisualizers = function() {
for (var i = 0, len = this.trackVisualizerArray.length; i < len; i++) {
this.trackVisualizerArray[i].initialize();
var canvasSideLength = max(min(windowWidth, windowHeight) * 0.95, min(displayWidth, displayHeight) * 0.5);
createCanvas(canvasSideLength, canvasSideLength);
backgroundColor = color(240);
metronome = Metronome.create(120);
myTrackSystem = new TrackSystem(32);
var sineOscillatorEnvelopeParameter = {
var sineTrackParameter = {
envelopeParameter: sineOscillatorEnvelopeParameter,
var sineTrack = new Track(sineTrackParameter);
myTrackSystem.trackArray.push(sineTrack);
var shortWhiteNoiseEnvelopeParameter = {
var shortWhiteNoiseTrackParameter = {
envelopeParameter: shortWhiteNoiseEnvelopeParameter,
var shortWhiteNoiseTrack = new Track(shortWhiteNoiseTrackParameter);
myTrackSystem.trackArray.push(shortWhiteNoiseTrack);
var longWhiteNoiseEnvelopeParameter = {
var longWhiteNoiseTrackParameter = {
envelopeParameter: longWhiteNoiseEnvelopeParameter,
notePattern: [false, false, false, false, true, false, false, false],
var longWhiteNoiseTrack = new Track(longWhiteNoiseTrackParameter);
myTrackSystem.trackArray.push(longWhiteNoiseTrack);
var brownNoiseEnvelopeParameter = {
var brownNoiseTrackParameter = {
envelopeParameter: brownNoiseEnvelopeParameter,
notePattern: [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false],
var brownNoiseTrack = new Track(brownNoiseTrackParameter);
myTrackSystem.trackArray.push(brownNoiseTrack);
myTrackSystem.trackVisualizerArray.push(TrackVisualizer.create(sineTrack, SineNoteVisualizer, width * 0.08, height * 0.16));
myTrackSystem.trackVisualizerArray.push(TrackVisualizer.create(shortWhiteNoiseTrack, ShortWhiteNoiseNoteVisualizer, width * 0.08, height * 0.24));
myTrackSystem.trackVisualizerArray.push(TrackVisualizer.create(longWhiteNoiseTrack, LongWhiteNoiseNoteVisualizer, width * 0.08, height * 0.32));
myTrackSystem.trackVisualizerArray.push(TrackVisualizer.create(brownNoiseTrack, BrownNoiseNoteVisualizer, width * 0.08, height * 0.40));
background(backgroundColor);
myTrackSystem.playNextNote();
function mousePressed() {
soundEnabled = !soundEnabled;