cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <!--  
Generate music and a visualization 
based on the text length and sentiment + 
the given curated list of rhythms and
note patterns + seeded randomality

---
This experiment uses so many awesome
components, see the "settings" for
more details about:

Gibber.js as a synth
and sequencer

AFINN sentiment analysis
to help with generating the music

Seeded randomality to be able
to generate the same music
given the same input

P5js to for the UI and 
visualization of the sound
-->
            
          
!
            
              html, body{
  margin:0;
  padding:0;
}

input{
  width: 30%;
  
}
            
          
!
            
              // sets which AFINN language the sentiment parser will use
var afinn = afinn_en;

//
// globals and config for the music and gibber.js
//
var drums;
var followDrums;
var followBass;
var fftSize = Math.pow(2,6);
var fft;
var randomInstrument;
var instrumentFM;
var bass;
var randomSynth;
// note: gibber drums:
// x : kick drum hit
// o : snare drum hit
// * : close hihat
// - : open hihat
// . : rest
var drumPatterns = ["x.x.x.xx", "x*o*x*o-","xxo.xoxx.","xxoxxoxx.xx-"];
var drumRhythms = [1/4,1/8,1/16];
var noteSequences = [[0,0,0,7,6,12,14],[0],[0,0,0,7,14],[5,7,1]];
var noteRhythms = [1/2,1,1/4,1/8,1/16];

var instruments = ["FM", "Pluck", "Synth2"];
var voicesFM = ["bass","drum2"];
var voicesSynth = ["bleep","rhodes"];

var bpm = 0;
var totalInstruments = 0;
var followList = [];
var instrumentOptions = [];
var input;
var instrumentList = [];
var moodCircles = [];
var tweetText = "This looks like something I might tweet!";


// randomness utilities
function randomRange(min, max) {
	return ((Math.random()*(max-min)) + min);
}
function randomFloor(min, max) {
	return Math.floor(randomRange(min, max));
}
function randomChoice(array){
  var choice = array[randomFloor(0,array.length)];
  // console.log("chose: ", choice);
  return choice;
}
function randomSequence(array, length){
  // console.log("randomSequence");
  var choices = [];
  for(i = 0; i<length; i++){
    choices.push(randomChoice(array));
  }
}

// sentiment utilities
function sentiment_to_bpm(score){
	var minimum_tempo = 50;
	var strength_multiplier = 16;
	return Math.round(Math.abs(score*strength_multiplier) + minimum_tempo);
}

function text_length_to_number_instruments(length){
	var minimum_instruments = 1;
	var per_instrument_count = 35;

	return Math.round(minimum_instruments + (length / per_instrument_count));
}
//

// p5 setup
function setup() {
	// p5 visual canvas
	createCanvas(windowWidth, windowHeight);
	colorMode(HSB, 255);
	background(0,0,0);

	input = createInput();
	input.value(tweetText);
  input.position((windowWidth * .10), windowHeight * .90);
  
  // fill(100,100,100);
  textSize(60);
  textFont("Helvetica");
  strokeWeight(1);
  text("generate music with text:", (windowWidth * .10), windowHeight * .10);

  text("enter text, click the generate button", (windowWidth * .10), windowHeight * .30);

  text("perform live: change the text to", (windowWidth * .10), windowHeight * .50);

  text("something new and back again", (windowWidth * .10), windowHeight * .70);

  var generateMusicButton = createButton("generate music using this text");
	generateMusicButton.position(input.x + input.width, input.y);
  generateMusicButton.mousePressed(createMusic);

	var stopMusicButton = createButton("stop");
	stopMusicButton.position(generateMusicButton.x + generateMusicButton.width, generateMusicButton.y);
  stopMusicButton.mousePressed(clearMusic);

	Clock.bpm = 0;
}

// p5 draw
function draw() {

	if(Clock.bpm > 0){
		// visual feedback: background based on drums
	  background(followDrums.getValue() * 210,  followDrums.getValue() * 210, followDrums.getValue() * 210);

		// more visual feedback
		moodCircles.forEach(function(mc, index, array){
			mc.update(index);
		});
	}
}

//
// visualization via mood circle
//

function MoodCircle(x,y,w,h, follow){
	this.x = x;
	this.y = y;
	this.width = w;
	this.height = h;
	this.hsv_v = randomRange(0,255);

	this.update = function(index){

    //get an fft ratio
    var fftValue = fft[index]/255;

		//update values
		this.width = fftValue * width;
		this.height = fftValue * height;
		this.x = fftValue * x;
		this.y = fftValue * y;

		//display results
		fill(follow.getValue() * 255, 255, this.hsv_v);
		// ellipse(this.x, this.y, this.width, this.height);
		rect(this.x, this.y, this.width, this.height);

	};
}

//
// music management
//

function clearMusic(){
	Clock.bpm = 0;
	Gibber.clear();
	instrumentList = [];
	instrumentOptions = [];
	moodCircles = [];
}

function createMusic(){

	clearMusic();

  // get the text (would be cool to someday feed this via tweet)
	console.log("input", input);
	console.log("input.value()", input.value())
	var inputValue = input.value();
	if(inputValue){
		tweetText = inputValue;
	}

	// prepare the seeded randomality
  Math.seedrandom(tweetText);

	// gather sentiment
	sentiment_result = sentiment(tweetText);
	console.log("sentiment: ", sentiment_result);
	console.log("sentiment.score: ", sentiment_result.score)

	// text length to number of instruments
	totalInstruments = text_length_to_number_instruments(tweetText.length);
	console.log("totalInstruments", totalInstruments);

	// todo: sentiment chooses which pool of drum beats
  commonRhythm = randomChoice(drumRhythms);
	console.log("commonRhythm: ", commonRhythm);

	// todo: sentiment chooses which pool of rhythms to choose from
  alternateRhythm = randomChoice(noteRhythms);
	console.log("alternateRhythm: ", alternateRhythm);

	// random chooses the note patterns
  commonNotePattern = randomChoice(noteSequences);
	console.log("commonNotePattern: ", commonNotePattern);

	bpm = sentiment_to_bpm(sentiment_result.score);
	Clock.bpm = bpm;
	console.log("beats per minute: ", bpm);

	instrumentOptions = [];
	function goDrums(){
		drums = EDrums(randomChoice(drumPatterns), commonRhythm);
		drumsAmp = randomRange(0.30, 0.80);
		followDrums = Follow(drums);
		followList.push(followDrums);
		drums.fadeIn(1, drumsAmp);
		instrumentList.push(drums);
	}
	instrumentOptions.push(goDrums);

	function goFM(){
		chosenVoice = randomChoice(voicesFM);
		console.log("FM voice melody bass: ", chosenVoice);
		instrumentFM = FM(chosenVoice)
		.note.seq(commonNotePattern, commonRhythm )
		instrumentFM.amp = randomRange(0.20, 0.80);
		instrumentList.push(instrumentFM);
	}
	instrumentOptions.push(goFM);

	function goInstrument(){
		chosenInstrument = randomChoice(instruments);
		console.log("TBD instrument voice bass: ", chosenInstrument);
		randomInstrument = window[chosenInstrument]()
		.note.seq(commonNotePattern, alternateRhythm )
		randomInstrumentAmp = randomRange(0.10, 0.60);
		randomInstrument.fadeIn(2, randomInstrumentAmp);
		instrumentList.push(randomInstrument);
	}
	instrumentOptions.push(goInstrument);

	function goSynth(){
		chosenSynth = randomChoice(voicesSynth);
		console.log("Synth: ", chosenSynth);
		randomSynth = Synth(chosenSynth, {amp:.35} )
		.chord.seq(Rndi(0,6,3), 1 )
		.fx.add(Delay());
		instrumentList.push(randomSynth);
	}
	instrumentOptions.push(goSynth);

	function goFM2(){
		chosenVoice = randomChoice(voicesFM);
		console.log("FM voice bass: ", chosenVoice);
		bass = FM(chosenVoice)
		.note.seq(commonNotePattern, commonRhythm);
		bassAmp = randomRange(0.20, 0.80);
		bass.fadeIn(2, bassAmp);
		followBass = Follow(bass);
		followList.push(followBass);
		instrumentList.push(bass);
	}
	instrumentOptions.push(goFM2);

  follow_main = Follow(Gibber.Master, 1024);
	followList.push(follow_main);

  fft = FFT(fftSize);

	var minimumCircleWidth = 50;
	var minInstruments = 1;
	var maxInstruments = instrumentOptions.length;

	if(totalInstruments > maxInstruments){
		totalInstruments = maxInstruments-1
	}else if(totalInstruments < minInstruments){
		totalInstruments = minInstruments;
	}

	for(var i=0; i<totalInstruments; i++){

		// activate an available instrument
		instrumentOptions[i]();

		var x = randomRange(0,windowWidth*.4);
		var y = randomRange(0,windowHeight*.5);
		var width = randomRange(minimumCircleWidth, windowWidth*.10);
		var height = width;
		var follow = randomChoice(followList);
		moodCircles.push(new MoodCircle(x,y,width,height,follow));
	}

}


            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console