musical pool
using d3 for a musical app
by hrabinowitz
HTML
<script src="http://mohayonao.github.io/timbre.js/timbre.js"></script>
<div id="wrap">
<div id="left" class="inner">
<h1>Musical pool</h1>
<p>This was made by <a href="http://twitter.com/nsonnad">Nikhil Sonnad</a>, but the concept was inspired by the beautiful <a href="http://www.ngv.vic.gov.au/whats-on/exhibitions/exhibitions/celeste-boursier-mougenot-clinamen"><em>clinamen</em> art installation by Céleste Boursier-Mougenot</a>, in which a small symphony is produced by porcelain bowls floating in a pool of water. The piece is now on display at the National Gallery of Victoria, Melbourne.</p>
<p>In this online 'version', white circles move about randomly and upon contact emit a pitch proportional to their size. You can drag them around yourself, as well, or change the musical scale used to generate pitch. (<a href="https://github.com/nsonnad/musical-plates">View source</a>)
<br />
<h4>Pitch lies along:</h4>
<div class="btn-group btn-group-vertical">
<button class="btn scale" id="pentatonic">Pentatonic scale</button>
<button class="btn scale" id="blues">Blues scale</button>
<button class="btn scale" id="aeolian">Aeolian mode</button>
<button class="btn scale" id="chromatic">Chromatic scale</button>
</div>
</div>
<div id="right" class="inner">
<div id="chart"></div>
</div>
<footer><p>Visual built in <a href="http://d3js.org/">d3.js</a>, audio powered by <a href="http://mohayonao.github.io/timbre.js/">timbre.js</a>. Runs best in Google Chrome.</p></footer>
</div>
JavaScript
var margin = 30,
w = 650 - margin * 2,
h = w,
radius = w / 2,
strokeWidth = 4,
hyp2 = Math.pow(radius, 2),
nodeBaseRad = 5,
nodeCount = 50;
function frequency(i) {
// formula for getting frequency according to distance from A440
var f = Math.pow(Math.pow(2, 1/12), i) * 220;
return f;
}
d3.selection.prototype.moveToFront = function() {
return this.each(function() { this.parentNode.appendChild(this);});
};
// scales, according to distance from A440
var freqs = {
'pentatonic': [36, 33, 31, 28, 26, 24, 21, 19, 16, 14, 12, 9, 7, 4, 2, 0],
'blues': [36, 34, 31, 30, 29, 27, 24, 22, 19, 18, 17, 14, 12, 10, 7, 6, 5, 3, 0],
'aeolian': [36, 34, 32, 31, 29, 27, 26, 24, 22, 20, 19, 17, 15, 14, 12, 10, 8, 7, 5, 3, 2, 0],
'chromatic': [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
};
// settings for timbre.js sounds
var sin = T("sin", {freq:440, mul:0.5}),
perc = T("perc", {a:30, r:300}, sin);
var svg = d3.select('#chart').append('svg')
.attr('width', w + margin * 2)
.attr('height', h + margin * 2)
.append('g')
.attr('transform', 'translate(' + margin + ',' + margin + ')');
var pool = svg.append('circle')
.style('stroke-width', strokeWidth * 2)
.attr({
class: 'pool',
r: radius,
cy: 0,
cx: 0,
transform: 'translate(' + w / 2 + ',' + h / 2 + ')'
});
function randomNodes(n, scale) {
var data = [],
range = d3.range(n),
notes = freqs[scale];
for (var i = range.length - 1; i >= 0; i--) {
r = Math.floor(Math.random() * (notes.length)) + 1
console.log(r)
data.push({
rad: r * 1.2,
freq: frequency(notes[r - 1])
});
}
return data;
}
var force = d3.layout.force()
.charge(0)
//.friction(0.92)
//.friction(0.5)
.friction(0.95)
.gravity(0)
.alpha(-5)
.size([w, h]);
var touched = svg.append("circle")
...