HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
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.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<header id="intro">
<section>
<h1>Procedurally Generated Music</h1>
<p>
This will infinitely generate a random song based on the <em>seed</em> that you provide it with.
Procedural generation ensures that the song will always be the exact
same song for the seed data you provide, but will be unique to any other seed.
</p>
<p>
Type anything into the input below and hit "Load" to generate and hear your song.
</p>
<input id="seed" type="text" placeholder="Seed" />
<button id="load">Load</button><br>
<small>
You can download a MIDI file of whatever you have generated in the browser when you hit "Pause".
To import into Ableton, you may need to rename the extension to ".mid".
The conversion is handled by ABCjs, and the midi accuracy varies with different software.
</small>
</section>
</header>
<main>
<button id="play">Play</button><br>
<p id="midi"></p>
<section id="staff-container">
<div id="staff"></div>
</section>
</main>
$staff-w: 800px;
$staff-h: 220px;
header {
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
z-index: 9;
background: rgba(0,0,0,0.6);
section {
position: absolute;
border-radius: 2px;
width: 90%;
max-width: 600px;
top: 50%; left: 50%;
background: white;
color: #222;
padding: 2rem;
transform: translate(-50%, -50%);
text-align: center;
}
h1 { margin-top: 0; }
p { text-align: left; }
small {
margin-top: 1rem;
color: #CCC;
display: inline-block;
}
}
#staff-container {
width: $staff-w;
overflow: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 0 4rem;
background-color: #FFF;
border-radius: 4px;
box-shadow: 0px 8px 16px rgba(0,0,0,0.1);
}
input, button {
border-radius: 2px;
line-height: 1.4;
margin: 0;
padding: 0.25em 0.5em;
}
input {
border: 1px solid darken(#FFF, 10%);
width: 280px;
}
button {
background: #444;
color: white;
border: 1px solid darken(#444, 10%);
}
#play, #midi {
position: absolute;
top: 1rem;
z-index: 8;
}
#play {
left: 50%;
transform: translateX(-50%);
}
#midi {
right: 1rem;
text-align: right;
margin: 0;
// ABCJS gives us an embed player that requires quicktime plugin. gross.
embed { position: absolute; top: -99999px; opacity: 0; pointer-events: none; }
a {
color: #444;
text-transform: uppercase;
font-size: 0.8em;
letter-spacing: 0.0125em;
}
}
#staff {
height: $staff-h * 2;
> div {
box-sizing: border-box;
height: $staff-h;
display: flex;
opacity: 1;
transition: opacity 250ms ease-out;
&:not(:nth-child(2)) path.symbol.l0 { opacity: 0; }
&:not(:nth-child(2)) { transition-delay: 250ms; }
&.hide { opacity: 0; }
&:first-child {
border-bottom: 1px solid #F0F0F0;
}
&:first-child > div { align-self: flex-end; }
}
}
@for $i from 3 through 6 {
@media (min-height: #{$i * $staff-h * 1.2}) {
#staff {
height: $staff-h * $i;
}
}
}
html, body {
height: 100%;
}
body {
background-color: #F0F0F0;
color: #222;
line-height: 1.7;
}
// console.clear();
var APP = {};
document.documentElement.addEventListener('mousedown', () => {
if (Tone.context.state !== 'running') Tone.context.resume();
});
// There's a bug that happens occasionally. Intercept it.
// a seed that triggers it: d12d21ddd
window.ABCJS.parse.each = function(a, d, c) {
var e = (a === undefined) ? 0 : a.length;
// for (var b = 0, e = a.length; b < e; b++) {
// ^^ this is the code that gets around it
for (var b = 0; b < e; b++) {
d.apply(c, [a[b], b])
}
}
document.getElementById("load").addEventListener("click", function(e) {
var seed = document.getElementById("seed").value;
if(seed) {
// if(seed.match(/^[A-Za-z0-9]+$/g)) {
document.getElementById("intro").style.display = "none";
APP.MusicGenerator.init(seed);
document.getElementById("play").focus();
document.getElementById("play").addEventListener("click", function(e) {
var $el = e.target;
if($el.getAttribute("data-playing") === "true") {
APP.MusicGenerator.pause();
$el.setAttribute("data-playing", "false");
$el.innerHTML = "Play";
APP.MusicGenerator.report();
} else {
APP.MusicGenerator.play();
$el.innerHTML = "Pause";
$el.setAttribute("data-playing", "true");
}
});
// } else {
// alert("Letters and numbers only");
// }
} else {
alert("Please enter a seed into the input");
}
});
(function() {
APP.MusicGenerator = {};
var _v_key = "WeAddaBabyEetsABoy"; // change this to refresh the seed
var _roots = "C Db D Eb E F F# G Ab A Bb".split(" ");
var _modes = "maj min".split(" ");
var _hold = "HOLD";
var _container = document.getElementById("staff-container");
var _staff = document.getElementById("staff");
var _midi = document.getElementById("midi");
var _min_oct = 2;
var _max_oct = 5;
var _playing = false;
var _res = 8;
var _curr_meas = 0;
var _curr_note = 0;
var _scale = [];
var _measures = [];
var _abc = "";
var _total_abc = "";
var _treb_abc = "";
var _bass_abc = "";
var _gen_meas = 0;
var _meas_gen = 4;
var _first_one = true;
var _bpm = null;
var _generator = null;
var _len_seqs = null;
var _mode = null;
var _output = null;
var _root = null;
var _seed = null;
var _synth = null;
APP.MusicGenerator.init = function(seed) {
_staff.innerHTML = "";
_seed = seed + _v_key;
_setGenerator(_seed);
_setKey();
if(!_synth) _setSynth();
_setTransport();
_genStaff();
_renderStaff();
_genLengthSequences();
_genScale();
_addMeasure(_meas_gen);
};
APP.MusicGenerator.play = playPlayer;
APP.MusicGenerator.pause = pausePlayer;
APP.MusicGenerator.report = reportData;
//////////////
// PUBLIC METHODS
//////////////
// play the player
function playPlayer() {
_midi.innerHTML = "";
Tone.Transport.start();
}
// pause the player
function pausePlayer() {
Tone.Transport.stop();
}
// reporting the data
function reportData() {
_output = {
root: _root, mode: _mode, resolution: _res,
scale: _scale, measures: _measures, abc: _total_abc
};
ABCJS.renderMidi(_midi, _total_abc , {}, {}, {});
console.log(_total_abc);
console.log(_output);
}
//////////////
// PRIVATE METHODS
//////////////
// get a unique random number generator based on the seed
function _setGenerator(seed) {
_generator = new Math.seedrandom(seed);
}
// get randomly generated key and mode
function _setKey() {
_root = _roots[Math.floor(_generator.quick() * _roots.length)];
var mode_index = Math.floor(_generator.quick() * _modes.length);
_mode = _modes[mode_index];
}
// play the bass and treble notes
function _playSong() {
var measure = _measures[_curr_meas];
if(measure) {
var treb = measure.treb[_curr_note];
var bass = measure.bass[_curr_note];
if(treb.chord) {
_synth.triggerAttackRelease(treb.chord, treb.len + "n");
}
if(bass.chord) {
_synth.triggerAttackRelease(bass.chord, bass.len + "n");
}
if(_curr_note + _curr_meas === 0) {
var hidden = document.querySelector(".hide");
if(hidden) hidden.className = "";
}
_curr_note++;
if(_curr_note >= _res) {
_curr_note = 0;
_curr_meas++;
if(_gen_meas % _meas_gen === 0) {
_addMeasure(_meas_gen);
} else if (_gen_meas % _meas_gen === 3) {
// scroll to the bottom of container
var hidden = document.querySelector(".hide");
if(hidden) hidden.className = "";
_utilScrollTo(_container, _container.scrollHeight, 500);
}
_gen_meas++;
}
} else {
pausePlayer();
}
}
// add a measure to the sequence
function _addMeasure(how_many, loaded) {
loaded = loaded || 0;
var get_measure = _getMeasure();
get_measure.then(function(measure) {
loaded++;
// console.log("_getMeasure", measure);
_measures.push(measure);
_staffABC(measure);
if(loaded < how_many) {
_addMeasure(how_many, loaded);
} else {
_renderStaff();
}
});
}
// generate a measure's worth of notes
function _getMeasure() {
return new Promise(function(resMeasure, rejMeasure) {
var data = {
scale: _scale.length / 2,
bass: { max_notes: 1, offset: 0 },
treb: { max_notes: 3, offset: Math.floor(_scale.length / 2) }
};
var measure = {
bass: [], treb: []
};
// for each clef
var get_treb = _getClef('treb', data);
get_treb.then(function(t) {
// console.log("_getClef: t", t);
measure.treb = measure.treb.concat(t);
var get_bass = _getClef('bass', data);
get_bass.then(function(b) {
// console.log("_getClef: b", b);
measure.bass = measure.bass.concat(b);
resMeasure(measure);
}, function(b) {
rejMeasure(b);
});
}, function(t) {
rejMeasure(t);
});
});
}
// generate notes for a clef
function _getClef(clef_name, data) {
return new Promise(function(resClef, rejClef) {
var _clef_generator = new Math.seedrandom(_generator.int32() + "");
// find a sequence
var seq = _len_seqs[Math.floor(_clef_generator.quick() * _len_seqs.length)];
// shuffle the sequence order
_utilShuffleArray(seq);
// set the data
var max_notes = data[clef_name].max_notes;
var note_offset = data[clef_name].offset;
var chord_promises = [];
// for each length in sequence
for(var s = 0; s < seq.length; s++) {
// generate x notes for a random bass count. no rests.
var note_count = Math.ceil(_clef_generator.quick() * max_notes);
// generate random note length
var note_length = seq[s];
chord_promises.push(_getChord(_clef_generator, data.scale, note_offset, note_count, note_length));
}
Promise.all(chord_promises).then(function(chords) {
var clef = [];
// console.log("_getChord: promises", chords);
for(var i = 0; i < chords.length; i++) {
var chord = chords[i];
clef.push(chord);
if(chord.len) {
var chord_length_i = _res / chord.len;
for(var e = 0; e < chord_length_i - 1; e++) {
clef.push(_hold);
}
}
}
resClef(clef);
}, function(chords) {
rejClef(chords);
});
});
}
// generate the chord notes
function _getChord(_clef_generator, scale, offset, note_count, note_length) {
return new Promise(function(resNotes, rejNotes) {
// used note indexes
var used_note_indexes = [];
var note_promises = [];
// generate the notes
for(var i = 0; i < note_count; i++) {
note_promises.push(_getNote(_clef_generator, scale, offset, used_note_indexes));
}
Promise.all(note_promises).then(function(chord) {
// console.log("_getNote: promises", chord);
var abc = _getABC(chord, note_length);
// add notes to measure
resNotes({ abc: abc, chord: chord, len: note_length });
}, function(chord) {
rejNotes(chord);
});
});
}
// generate a note
function _getNote(_clef_generator, scale, offset, used_note_indexes) {
return new Promise(function(resNote, rejNote) {
// add the note to the chord
resNote(_note());
function _note() {
// get a random index
var index = Math.floor(_clef_generator.quick() * scale) + offset;
// for measuring note is a "good" one.
var good_note = true;
// ensure we havent selected this index
if(used_note_indexes.indexOf(index) !== -1) good_note = false;
// or one below it
if(index > 0 && used_note_indexes.indexOf(index - 1) !== -1) good_note = false;
// or one above it
if(used_note_indexes.indexOf(index + 1) !== -1) good_note = false;
// if we found a good note
if(good_note) {
// add the index to the used array
used_note_indexes.push(index);
// grab the note
var note = _scale[index];
// build the tone version of the note
var n = note.note + note.octave;
return n;
} else {
return _note();
}
}
});
}
//////////////
// ABC MUSICAL NOTATION
//////////////
// get abc notation for notes at a certain length
function _getABC(notes, len) {
if(!len) return "";
var str = notes.length > 1 ? "[" : "";
for(var n = 0; n < notes.length; n++) {
str += _ABCNotation(notes[n], len);
}
str += notes.length > 1 ? "]" : "";
return str;
}
// render the current abc to staff
function _renderStaff() {
var $par = document.createElement("div");
if(_first_one) {
_first_one = false;
} else {
$par.className = "hide";
}
var $el = document.createElement("div");
// beats per second times four beats * measures per line * 1 second
$par.appendChild($el);
_staff.appendChild($par);
_abc += _treb_abc;
_abc += "\n" + _bass_abc;
var cleaned = _abc;
_total_abc += cleaned;
_abc = _genStaffHeader();
_treb_abc = "[V: 1] ";
_bass_abc = "[V: 2] ";
_renderABC($el, cleaned);
}
function _renderABC(el, abc) {
console.log(abc);
ABCJS.renderAbc(el, abc, {}, {
staffwidth: 800,
paddingright: 0,
paddingleft: 0,
scale: 1,
add_classes: true
}, {});
}
// take a measure and generate the abc output
function _staffABC(measure) {
var abc = "";
// TODO: every two measures new line with _gen_meas, _treb_abc, _bass_abc
for(var i = 0; i < measure.treb.length; i++) {
if(measure.treb[i].abc) {
_treb_abc += measure.treb[i].abc + " ";
}
}
for(var i = 0; i < measure.bass.length; i++) {
if(measure.bass[i].abc) {
_bass_abc += measure.bass[i].abc + " ";
}
}
_treb_abc = _treb_abc.replace(/ $/g, "|");
_bass_abc = _bass_abc.replace(/ $/g, "|");
// abc += [treb, bass].join("\n");
// return abc;
}
// set the initial staff
function _genStaff() {
_abc = [
"M:/4/4",
"O:I",
"R:R",
"Q:1/4=" + _bpm,
"",
"X:2",
"T:" + _seed.replace(_v_key, ""),
"C:" + _root.replace(/b$/g, "♭") + " " + _mode + ", " + _bpm + " bpm",
].join("\n");
}
// generate the staff header
function _genStaffHeader() {
return [
"K:" + _root + _mode,
"L:1/" + _res,
"%%staves {1 2}",
"V: 1 clef=treble",
"V: 2 clef=bass",
""
].join("\n");
}
// abc notation
function _ABCNotation(note, length) {
// note = note.replace(/([A-G])b/,"_$1");
// TODO: know if this is needed based on mode.
note = note.replace(/([A-G])b/,"$1");
note = note
.replace(/(.)1/, "$1,,,")
.replace(/(.)2/, "$1,,")
.replace(/(.)3/, "$1,")
.replace(/(.)4/, "$1");
if(note.match(/(.)5|6/)) {
note = note
.replace(/(.)5/, "$1")
.replace(/(.)6/, "$1'")
.toLowerCase();
}
note += _res / length;
return note;
}
//////////////
// DATA
//////////////
// generate the scale
function _genScale() {
// generate the scale
var _steps_lib = _genStepsLib();
var _notes_lib = _genNotesLib();
var steps = _steps_lib;
var start = _notes_lib.indexOf(_root + _min_oct);
var last_note = (_max_oct - _min_oct) * 8;
for(var o = 0; o < last_note; o++) {
var index = start + steps[o % steps.length] + (Math.floor(o / steps.length) * 12);
var note = _notes_lib[index];
if(note) {
_scale.push({ note: note.match(/[^\d]+/)[0], octave: note.match(/\d/)[0] });
}
}
if(!_scale.length) console.error("Too high up. Lower the octave or note count.");
}
// potential combinations of note lengths
// this could be done programmatically.
function _genLengthSequences() {
_len_seqs = [
[1],
[2,2],
[2,4,4],
[2,4,8,8],
[4,4,4,4],
[2,8,8,8,8],
[4,4,4,8,8],
[8,8,8,8,8,8,8,8],
// [2,4,8,16,16],
// [2,4,16,16,16,16],
// [2,8,8,8,16,16],
// [2,8,8,16,16,16,16],
// [2,8,16,16,16,16,16,16],
// [2,16,16,16,16,16,16,16,16],
// [4,4,4,8,16,16],
// [4,4,4,16,16,16,16],
// [4,4,8,8,16,16,16,16],
// [4,4,8,16,16,16,16,16,16],
// [4,4,16,16,16,16,16,16,16,16],
// [4,8,8,16,16,16,16,16,16,16,16],
// [4,8,16,16,16,16,16,16,16,16,16,16],
// [4,16,16,16,16,16,16,16,16,16,16,16,16],
// [8,8,8,8,8,8,8,16,16],
// [8,8,8,8,8,8,16,16,16,16],
// [8,8,8,8,8,16,16,16,16,16,16],
// [8,8,8,8,16,16,16,16,16,16,16,16],
// [8,8,8,16,16,16,16,16,16,16,16,16,16],
// [8,8,16,16,16,16,16,16,16,16,16,16,16,16],
// [8,16,16,16,16,16,16,16,16,16,16,16,16,16,16],
// [16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16],
];
}
// set the notes for the key
function _genStepsLib() {
// generate major and minor integer step arrays from whole/half notation
// whole/half note steps for minor and major
var mode = {
// ionian: "W W H W W W H",
// dorian: "W H W W W H W",
// phrygian: "H W W W H W W",
// lydian: "W W W H W W H",
// mixolydian: "W W H W W H W",
// aeolian: "W H W W H W W",
// locrian: "H W W H W W W",
maj: "W W H W W W H",
min: "W H W W H W W",
}[_mode].split(" ");
// start each with root step
var steps = [0];
// loop through
var step = 0;
for(var s = 0; s < mode.length; s++) {
var key = mode[s];
if(key === "W") {
steps.push(steps[s] + 2);
} else if(key == "WH") {
steps.push(steps[s] + 3);
} else {
steps.push(steps[s] + 1);
}
step++;
}
return steps;
}
// generate every possible note/octave pairing in order
function _genNotesLib() {
var notes = [];
for(var i = 0; i < 9; i++) {
var n = "C Db D Eb E F Gb G Ab A Bb B".split(" ");
for(var x = 0; x < n.length; x++) notes.push(n[x] + i);
}
return notes;
}
//////////////
// TONE.js
//////////////
// set the tone js transport
function _setTransport() {
_bpm = Math.round(_generator.quick() * 60) + 60;
Tone.Transport.bpm.value = _bpm;
Tone.Transport.scheduleRepeat(function(time) {
_playSong();
}, _res + "n");
}
// set the synthesizer
function _setSynth() {
// master compression
var multiband = new Tone.MultibandCompressor({
lowFrequency: 200,
highFrequency: 1300,
low: { threshold: -12 }
});
var reverb = new Tone.Freeverb();
reverb.roomSize.value = 0.6;
reverb.wet.value = 0.2;
Tone.Master.chain(reverb, multiband);
// _synth = new Tone.PolySynth(6, Tone.SimpleAM).toMaster();
_synth = new Tone.PolySynth(6, Tone.SimpleSynth).toMaster();
_synth.set({
oscillator: {
type: "triangle"
},
envelope: {
attack: 0.05,
decay: 0.1,
sustain: 0.3,
release: 1
}
});
// _synth.set({
// envelope: {
// attack: 0.5,
// release: 0.5
// }
// });
// _synth.set("carrier", {
// volume: -8,
// oscillator: {
// type: "triangle8",
// partials: [1, 0.2, 0.01]
// },
// envelope: {
// attack: 0.05,
// decay: 0.02,
// sustain: 0.6,
// release: 0.8
// }
// });
// _synth.set("modulator", {
// volume: -16,
// oscillator: {
// type: "triangle8",
// detune: 0,
// phase: 90,
// partials: [1, 0.2, 0.01]
// },
// envelope: {
// attack: 0.05,
// decay: 0.01,
// sustain: 1,
// release: 1
// }
// });
}
//////////////
// UTILS
//////////////
// shuffle an array
// http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function _utilShuffleArray(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(_generator() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
function _utilScrollTo(element, to, duration) {
if (duration <= 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
_utilScrollTo(element, to, duration - 10);
}, 10);
}
}());
Also see: Tab Triggers