summaryrefslogtreecommitdiff
path: root/env.js
diff options
context:
space:
mode:
Diffstat (limited to 'env.js')
-rw-r--r--env.js402
1 files changed, 402 insertions, 0 deletions
diff --git a/env.js b/env.js
new file mode 100644
index 0000000..d1989c6
--- /dev/null
+++ b/env.js
@@ -0,0 +1,402 @@
+var environment = (function(){
+
+ var environment = {}
+
+ var image_index = -1
+ var snap, dot_grid, polysynth
+ var strided_wires = [], wires = []
+ var scale = [], root, tet, interval
+ var dot_color = "#333"
+
+ var palette = [
+ "#ff0000",
+ "#00ffff",
+ "#0000ff",
+ "#ff8800",
+ "#ffff00",
+ ]
+
+ environment.init = function(){
+ environment.ready()
+ }
+ environment.ready = function(){
+ environment.build()
+ environment.bind()
+ }
+ environment.build = function(){
+ environment.scale()
+ snap = new Snap (window.innerWidth, window.innerHeight - $("#controls").height())
+
+ dot_grid = new DotGrid ()
+ dot_grid.load()
+
+ polysynth = new Tone.PolySynth(8, Tone.synth)
+ polysynth.set({
+ oscillator: { type: $("#waveform").val() },
+ envelope:{
+ attack: 0.01,
+ decay: 0.1,
+ sustain: 0.3,
+ release: 0.2,
+ }
+ })
+ var comp = new Tone.Compressor(-30, 3).toMaster()
+ var reverb = new Tone.Freeverb ()
+ reverb.wet.value = 0.05
+ polysynth.connect(reverb)
+ reverb.connect(comp)
+ // environment.stride()
+ // environment.randomize()
+ }
+ environment.bind = function(){
+ $(window).focus(environment.focus)
+ $(window).blur(environment.blur)
+ $(window).mousemove(environment.mousemove)
+ window.addEventListener("keydown", environment.keydown, true)
+ $("#waveform").on("input", environment.setWaveform)
+ $("#x").on("keydown", environment.stride)
+ $("#y").on("keydown", environment.stride)
+ $("#rand").on("input", environment.randomize)
+ $("#tet").on("input", environment.scale)
+ $("#root").on("input", environment.scale)
+ $("#interval").on("input", environment.scale)
+ $("#use_scale").on("change", environment.use_scale)
+ $(snap.node).on("mousedown", dot_grid.mousedown.bind(dot_grid))
+ $(snap.node).on("mouseup", dot_grid.mouseup.bind(dot_grid))
+ }
+ environment.blur = function(){
+ last_p = null
+ }
+ environment.focus = function(){
+ }
+ environment.use_scale = function(){
+ use_scale = $("#use_scale").get(0).checked
+ }
+ environment.keydown = function(e){
+ if (e.altKey || e.ctrlKey || e.metaKey) {
+ e.stopPropagation()
+ return
+ }
+ if (e.keyCode == 69) { // charcode: e
+ e.preventDefault()
+ }
+ if (document.activeElement instanceof HTMLInputElement &&
+ (e.keyCode in key_numbers)) {
+ e.stopPropagation()
+ return
+ }
+ if (! (e.keyCode in keys)) return
+ var i = keys[e.keyCode]
+ if (e.shiftKey) i += letters.length
+ var wire = wires[ i % wires.length ]
+ wire && wire.play()
+ }
+ environment.setWaveform = function(){
+ var w = $("#waveform").val()
+ polysynth.set({ oscillator: { type: w } })
+ }
+ environment.last_p = { x: 0, y: 0 }
+ environment.positionFromEvent = function(e){
+ var offset = $(snap.node).offset()
+ var p = {
+ x: e.pageX - offset.left,
+ y: e.pageY - offset.top,
+ }
+ return p
+ }
+ environment.mousemove = function(e){
+ var p = environment.positionFromEvent(e)
+ if (dot_grid.active) {
+ dot_grid.active.update({ point: p, muted: false, duration: 0.2 })
+ }
+ else {
+ wires.forEach(function(wire){
+ wire.intersect(p, environment.last_p)
+ })
+ }
+ environment.last_p = p
+ }
+ environment.stride = function(e){
+ var dx = parseFloat( $("#x").val() )
+ var dy = parseFloat( $("#y").val() )
+ environment.reset()
+ if (! dx && ! dy) return
+ if (dx < 1.1 && ! dy) return
+ var x0 = 0, x1 = dx, y0 = 0, y1 = dy
+ var wire
+ do {
+ wire = new Wire ({
+ head: { x: x0, y: y0 },
+ tail: { x: x1, y: y1 },
+ color: { h: Math.round(y0 / dot_grid.dots[0].length * 180 + 180), s: 100, l: 50 },
+ integer: true,
+ })
+ strided_wires.push(wire)
+ if (dx || ! dy) x0 += 1
+ y0 += 1
+ x1 += dy
+ y1 += dx
+ }
+ while ( y0 < dot_grid.dots.length)
+ }
+ environment.reset = function(){
+ strided_wires.forEach(function(wire){
+ wire.remove()
+ })
+ strided_wires = []
+ }
+ environment.randomize = function(){
+ var n = parseInt( $("#rand").val() )
+ environment.reset()
+ var x0, y0, x1, y1
+ while (n--) {
+ x0 = randint( dot_grid.dots.length )
+ x1 = randint( dot_grid.dots.length )
+ y0 = randint( dot_grid.dots[0].length )
+ y1 = randint( dot_grid.dots[0].length )
+ wire = new Wire ({
+ head: { x: x0, y: y0 },
+ tail: { x: x1, y: y1 },
+ color: { h: Math.round(y0 / dot_grid.dots[0].length * 180 + 180), s: 100, l: 50 },
+ integer: true,
+ })
+ strided_wires.push(wire)
+ }
+ }
+ environment.scale = function(e){
+ if (e && e.keyCode && ! key_numbers(e.keyCode)) {
+ e.preventDefault()
+ return
+ }
+ var ratio, n
+ tet = parseFloat( $("#tet").val() )
+ root = parseFloat( $("#root").val() )
+ interval = parseInterval( $("#interval").val() )
+ ratio = Math.pow( interval, 1/tet )
+ n = root
+ scale = [n]
+ for (var i = 0; i < tet; i++) {
+ n *= ratio
+ scale.push(n)
+ }
+ wires.forEach(function(wire){
+ wire.updateColor(true)
+ })
+ }
+ // quantize a frequency to the scale
+ environment.quantize = function(f, get_index){
+ if (f == 0) return 0
+ var scale_f = f
+ var pow = 0
+ while (scale_f < root) {
+ scale_f *= interval
+ pow -= 1
+ }
+ while (scale_f > root*interval) {
+ scale_f /= interval
+ pow += 1
+ }
+ for (var i = 0; i < scale.length; i++) {
+ if (scale_f > scale[i]) continue
+ scale_f = scale[i]
+ break
+ }
+ if (get_index) { return i }
+ scale_f *= Math.pow(2, pow)
+ // console.log(scale_f)
+ return scale_f
+ }
+
+ function DotGrid (opt){
+ this.opt = defaults(opt, {
+ dot_min: 2,
+ dot_max: 10,
+ dot_spacing: 19,
+ mouse_radius: 200,
+ wave_width: 200,
+ fill: "#ffffff",
+ speed: 4,
+ ease: 10,
+ })
+ this.active = null
+ this.group = snap.g()
+ this.wire_group = snap.g()
+ this.wire_group.attr({ "style": "pointer-events: none" })
+ this.dots = []
+ }
+ DotGrid.prototype.indexToPoint = function(p){
+ var dx = this.opt.dot_spacing
+ var q = {}
+ q.x = (p.x + 1/2) * dx
+ q.y = (p.y + 1/2) * dx
+ return q
+ }
+ DotGrid.prototype.quantize = function(p){
+ var dx = this.opt.dot_spacing
+ var q = {}
+ q.x = quantize(p.x - dx/2, dx) + dx/2
+ q.y = quantize(p.y - dx/2, dx) + dx/2
+ return q
+ }
+ DotGrid.prototype.load = function(){
+ var dots = this.dots
+ var dx = dy = this.opt.dot_spacing
+ var x, y, i, j, u, r, a
+
+ var dot_min = this.opt.dot_min
+ var dot_max = this.opt.dot_max
+
+ var w = this.w = window.innerWidth
+ var h = this.h = window.innerHeight - $("#controls").height() - dy
+
+ for (x = dx/2; x < w; x += dx) {
+ a = []
+ dots.push(a)
+ for (y = dy/2; y < h; y += dy) {
+ dot = this.group.circle(x, y, 4).attr({
+ fill: dot_color,
+ })
+ a.push(dot)
+ }
+ }
+ }
+ DotGrid.prototype.mousedown = function(e){
+ var p = environment.positionFromEvent(e)
+ var q = this.quantize(p)
+ if (! this.active) {
+ this.active = new Wire ({
+ head: q
+ })
+ }
+ else if (this.active.head.x == q.x && this.active.head.y == q.y) {
+ return
+ }
+ else if (this.active) {
+ e.preventDefault()
+ this.active.setTail(q)
+ this.active = null
+ }
+ }
+ DotGrid.prototype.mouseup = function(e){
+ if (this.active && this.active.length > 0) {
+ var q = this.quantize(environment.last_p)
+ this.active.setTail(q)
+ this.active = null
+ }
+ }
+
+ function Wire (opt){
+ this.opt = opt
+ if (opt.integer) {
+ opt.head = dot_grid.indexToPoint(opt.head)
+ if (opt.tail) {
+ opt.tail = dot_grid.indexToPoint(opt.tail)
+ }
+ }
+ if (opt.color && opt.color.h) {
+ opt.color = Snap.hsl(opt.color.h, opt.color.s, opt.color.l)
+ }
+ else if (! opt.color) {
+ opt.color = choice(palette)
+ }
+ this.length = 0
+ this.head = opt.head
+ var path_str = "M" + 0 + "," + 0
+ this.path = dot_grid.wire_group.path(path_str).attr({
+ stroke: opt.color,
+ fill: "none",
+ strokeWidth: 3,
+ })
+ if (opt.tail) {
+ this.setTail(opt.tail, true)
+ }
+ }
+ Wire.prototype.update = function(opt){
+ var q = dot_grid.quantize(opt.point)
+ var length = this.length = dist(this.head.x, this.head.y, q.x, q.y)
+ var theta = angle(this.head.x, this.head.y, q.x, q.y) * 180 / Math.PI
+ var path_str = "M" + 0 + "," + 0
+ + "t" + length.toFixed(2) + "," + 0
+ var tran_str = "translate(" + this.head.x + "," + this.head.y + ") "
+ + "rotate(" + theta + ")"
+ var color = this.updateColor()
+ this.path.attr({
+ d: path_str,
+ transform: tran_str,
+ stroke: color,
+ })
+ if (! opt.muted) {
+ this.play(opt.duration)
+ }
+ return length
+ }
+ Wire.prototype.updateColor = function(should_set){
+ var index = this.index()
+ var color = Snap.hsl(mod(index / tet * 240 + 180, 360), 100, 50)
+ if (should_set) {
+ this.path.attr({
+ stroke: color,
+ })
+ }
+ return color
+ }
+ Wire.prototype.setTail = function(p, muted){
+ this.tail = p
+ this.update({ point: p, muted: true })
+ wires.push(this)
+ }
+ Wire.prototype.intersect = function (a,b) {
+ if (a.x - b.x == 0 || a.y - b.y == 0) return
+ if (doLineSegmentsIntersect(a, b, this.head, this.tail)) {
+ this.play()
+ }
+ }
+ Wire.prototype.index = function(){
+ var f = this.length / 340.29 * root
+ return environment.quantize( f, true )
+ }
+ Wire.prototype.play = function(duration){
+ var f = this.length / 340.29 * root
+ if (use_scale) {
+ f = environment.quantize(f)
+ }
+ polysynth.triggerAttackRelease(f, duration || randrange(1.0, 2.5))
+ }
+ Wire.prototype.remove = function(){
+ this.path.remove()
+ wires.splice(wires.indexOf(this), 1)
+ }
+
+ environment.update = function(t){
+ }
+
+ var keys
+ var keys = {}
+ var key_numbers = {}
+ var letters = "qwertyuiop[]\\asdfghjkl;'zxcvbnm,./"
+ var numbers = "1234567890";
+ letters.toUpperCase().split("").map(function(k,i){
+ keys[k.charCodeAt(0)] = i
+ })
+ numbers.split("").map(function(k,i){
+ keys[k.charCodeAt(0)] = true
+ key_numbers[k.charCodeAt(0)] = true
+ })
+
+ function parseInterval (s){
+ if (typeof s == "number") return s
+ if (! s.indexOf("/") == -1) return parseInt(s)
+ var pp = s.split("/")
+ var num = parseInt(pp[0])
+ var den = parseInt(pp[1])
+ if (isNaN(num)) return 2
+ if (isNaN(den) || den == 0) return num
+ if (num == den) return 2
+ if (num < den) return den/num
+ return num / den
+ }
+
+ return environment
+
+})()
+