function init() { canvas = document.getElementById('canvas'); width = canvas.clientWidth; height = canvas.clientHeight; canvas.width = width; canvas.height = height; ctx = canvas.getContext('2d'); controlLock = false; rotX = 0; rotY = 0; rotZ = 0; animate = 0; document.controls = {}; var knobElements = document.getElementsByClassName("knob"); for (var i = 0; i < knobElements.length; i++) { knobInit(knobElements[i]); } document.body.onmousemove = bodyOnMouseMove; document.body.onmouseup = bodyOnMouseUp; setInterval("render()", 50); // calculate();render(); } function calculate() { with(Math) { for (id in document.controls) { var ctrl = document.controls[id]; ctrl.lfoPhase += 10 * ctrl.lfoRate; switch (ctrl.lfoType) { case 'sin': ctrl.lfoValue = sin(2 * PI * ctrl.lfoPhase); break; case 'up': ctrl.lfoValue = (ctrl.lfoPhase % 1.0); break; case 'down': ctrl.lfoValue = -(ctrl.lfoPhase % 1.0); break; case 'rnd': if (ctrl.lfoPhase >= 1) { ctrl.lfoValue = 2 * random() - 1; ctrl.lfoPhase = ctrl.lfoPhase % 1.0; } break; default: break; } var value = ctrl.knobValue + ctrl.lfoAmount * ctrl.lfoValue; eval(id + " = " + value); } } } function render() { calculate(); with(Math) { rotX = rotX + 0.1 * (-0.5 + rotateX); rotY = rotY + 0.1 * (-0.5 + rotateY); rotZ = rotZ + 0.1 * (-0.5 + rotateZ); animate = animate + anim * 30; sp_t = max(sp_t, 0.01); sp_T = max(sp_T, 0.001); sp_T2 = max(sp_T2, 0.01); sp_k = max(sp_k, 0.000001); ctx.globalAlpha = pow(bgAlpha, 3); ctx.fillStyle = 'rgb(' + floor(bgRed * 255) + ',' + floor(bgGreen * 255) + ',' + floor(bgBlue * 255) + ')'; ctx.fillRect(0, 0, width, height); ctx.globalAlpha = alpha; ctx.strokeStyle = 'rgb(' + floor(red * 255) + ',' + floor(green * 255) + ',' + floor(blue * 255) + ')'; ctx.lineWidth = max(pow(lineWidth, 7) * 200, 0.1); ctx.beginPath(); var s = max(sp_t * sp_k, 0.001); var count = (2 * sp_T / s) * sp_T2 * 100; var xx = 0; var yy = 0; var xxx = 0; var yyy = 0; var multicolormode = (zScale > 0) ? true : false; var scale = height; for (var i = -2; i <= count + 1; i++) { var t = (animate + i) * PI * s; var xo = (1 - sp_k) * cos(t) + (-2 + 4 * sp_l) * sp_k * cos(t * (1 - sp_k) / sp_k); var yo = (1 - sp_k) * sin(t) - (-2 + 4 * sp_l) * sp_k * sin(t * (1 - sp_k) / sp_k); var zo = 0; var rx = rotX + t * x3D; var ry = rotY + t * y3D; var rz = rotZ; var x = xo * (cos(ry) * cos(rz)) + yo * (-cos(rx) * sin(rz) + sin(rx) * sin(ry) * cos(rz)) + zo * (sin(rx) * sin(rz) + cos(rx) * sin(ry) * cos(rz)); var y = xo * (cos(ry) * sin(rz)) + yo * (cos(rx) * cos(rz) + sin(rx) * sin(ry) * sin(rz)) + zo * (-sin(rx) * cos(rz) + cos(rx) * sin(ry) * sin(rz)); var z = xo * (-sin(ry)) + yo * (sin(rx) * cos(ry)) + zo * (cos(rx) * cos(ry)); //var x = xo*cos(rz) - yo*sin(rz); //var y = xo*sin(rz) + yo*cos(rz); x = scale * sp_R * x + (posX * width); y = scale * sp_R * y + (posY * height); if (i <= 0) { ctx.moveTo(x, y); } else { if (multicolormode) { ctx.beginPath(); var cz = max(min((z + 2 * zOffset - 1) * 2 * zScale, 1), -1); var cr = floor(255 * (z < 0 ? bgRed + (1 + cz) * (red - bgRed) : red + cz * (1 - red))); var cg = floor(255 * (z < 0 ? bgGreen + (1 + cz) * (green - bgGreen) : green + cz * (1 - green))); var cb = floor(255 * (z < 0 ? bgBlue + (1 + cz) * (blue - bgBlue) : blue + cz * (1 - blue))); ctx.strokeStyle = 'rgb(' + cr + ',' + cg + ',' + cb + ')'; ctx.moveTo(xxx, yyy); ctx.lineTo(xx, yy); ctx.lineTo(x, y); ctx.stroke(); } else { ctx.lineTo(x, y); } } xxx = xx; yyy = yy; xx = x; yy = y; } ctx.stroke(); } controlLock = false; } function bodyOnMouseMove(event) { if (!controlLock && document.dragElement) { controlLock = true; document.dragElement.ondrag(event); } } function bodyOnMouseUp(event) { if (document.dragElement) { document.dragElement = null; } } function eGetElement(event) { if (window.event) // IE return event.srcElement; else if (event.target) // Netscape/Firefox/Opera return event.target; return null; } function eGetX(event) { var nRet = 0; var evt = event ? event : window.event; if (evt.pageX) nRet = evt.pageX; else if (evt.clientX) nRet = evt.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; return nRet; } function eGetY(event) { var nRet = 0; var evt = event ? event : window.event; if (evt.pageY) nRet = evt.pageY; else if (evt.clientY) nRet = evt.clientY + document.body.scrollTop + document.documentElement.scrollTop; return nRet; } function knobInit(element) { if (!element.id) { var id = ""; do { id = "knob" + Math.round(Math.random() * 10000, 0); } while (document.getElementById(id) != null); element.id = id; } var lfoTypes = "<option>sin</option><option>up</option><option>down</option><option>rnd</option>"; var initValue = Math.min(Math.max(Number(element.innerHTML), 0), 1); document.controls[element.id] = { knobValue: initValue, knobMinValue: 0, knobMaxValue: 0, lfoType: "sin", lfoRate: 0, lfoAmount: 0, lfoPhase: 0, lfoValue: 0 }; element.innerHTML = "<div class='knob_label'>" + element.title + "</div>" + "<div class='knob_body' id='" + element.id + "_body'>" + "<div class='knob_knob' id='" + element.id + "_knob'>" + "<div class='knob_arrow'> </div></div></div>" + "<input class='knob_min_input' id='" + element.id + "_min_input' value='0'>" + "<input class='knob_max_input' id='" + element.id + "_max_input' value='1'>" + "<input class='knob_input' id='" + element.id + "_input'>" + "<div class='knob_lfo_type' id='" + element.id + "_lfo_type'>LFO: sin</div>" + "<select class='knob_lfo_type_select' onchange='knobOnLfoChange(this)' id='" + element.id + "_lfo_type_select'>" + lfoTypes + "</select>" + "<div class='knob_lfo' id='" + element.id + "_rate' title='Rate' style='margin-right:1px'>" + "<div class='knob_lfo_body' id='" + element.id + "_rate_body'>" + "<div class='knob_lfo_knob' id='" + element.id + "_rate_knob'>" + "<div class='knob_lfo_arrow'> </div></div></div>" + "<div class='knob_lfo_label'>rate</div>" + "</div>" + "<div class='knob_lfo' id='" + element.id + "_modamt' title='Amount'>" + "<div class='knob_lfo_body' id='" + element.id + "_modamt_body'>" + "<div class='knob_lfo_knob' id='" + element.id + "_modamt_knob'>" + "<div class='knob_lfo_arrow'> </div></div></div>" + "<div class='knob_lfo_label'>amt</div>" + "</div>"; var knobs = [element.id, element.id + "_rate", element.id + "_modamt"]; var values = [initValue, 0, 0]; for (var i = 0; i < knobs.length; i++) { var knob = document.getElementById(knobs[i]); var knobBody = document.getElementById(knobs[i] + "_body"); knob.value = values[i]; knobBody.onclick = knobOnClick; knobBody.onmousemove = knobOnMouseMove; knobBody.onmousedown = knobOnMouseDown; knobBody.onmouseout = knobOnMouseOut; knob.ondrag = knobOnDrag; knobUpdate(knob); } } function knobGetElement(event) { var element = eGetElement(event); while (element.className != "knob" && element.className != "knob_lfo") element = element.parentNode; return element; } function knobUpdate(element) { var knob = document.getElementById(element.id + "_knob"); knob.style.MozTransform = "rotate(" + (270 * Number(element.value) - 45) + "deg)"; knob.style.WebkitTransform = "rotate(" + (270 * Number(element.value) - 45) + "deg)"; var input = document.getElementById(element.id + "_input"); if (input) { input.value = element.value; } if (element.id in document.controls) document.controls[element.id].knobValue = element.value; else if (element.parentNode.id in document.controls) { // lfo knob var expValue = Math.pow(element.value, 3); if (element.title == "Amount") document.controls[element.parentNode.id].lfoAmount = expValue; else if (element.title == "Rate") document.controls[element.parentNode.id].lfoRate = expValue; } } function knobOnLfoChange(element) { var lfoType = element.value; document.getElementById(element.parentNode.id + "_lfo_type").innerHTML = "LFO: " + lfoType; document.controls[element.parentNode.id].lfoType = lfoType; } function knobOnDrag(event) { var x = eGetX(event) - document.dragX; var y = eGetY(event) - document.dragY; var element = document.dragElement; element.value = Math.max(Math.min(Number(document.dragValue) - (Math.pow(y / 50, 3)), 1), 0); knobUpdate(element); } function knobOnClick(event) {} function knobOnMouseMove(event) {} function knobOnMouseDown(event) { document.dragElement = knobGetElement(event); document.dragX = eGetX(event); document.dragY = eGetY(event); document.dragValue = document.dragElement.value; } function knobOnMouseOut(event) {} init();
<canvas id="canvas"></canvas> <div id="copyright">© Tim Wessman, 2012</div> <div id="showHide" onclick="var controls = document.getElementById('controls');controls.style.display = controls.style.display == 'none' ? 'block' : 'none';this.innerHTML = controls.style.display == 'none' ? 'showControls' : 'hideControls';">hideControls</div> <div id="controls"> <div class="knob_group"> <div class="label"><div class="label_text">spirograph</div></div> <div class="knob" id="sp_l" title="L">0.71</div> <div class="knob" id="sp_k" title="K">0.71</div> <div class="knob" id="sp_T" title="length">0.2</div> <div class="knob" id="sp_T2" title="length2">0.2</div> <div class="knob" id="sp_R" title="size">0.35</div> <div class="knob" id="lineWidth" title="width">0.4</div> </div> <div class="knob_group"> <div class="label"><div class="label_text">color</div></div> <div class="knob" id="red" title="red">0.15</div> <div class="knob" id="green" title="green">1</div> <div class="knob" id="blue" title="blue">0.90</div> <div class="knob" id="alpha" title="alpha">0.5</div> </div> <div class="knob_group"> <div class="label"><div class="label_text">background</div></div> <div class="knob" id="bgRed" title="red"></div> <div class="knob" id="bgGreen" title="green"></div> <div class="knob" id="bgBlue" title="blue"></div> <div class="knob" id="bgAlpha" title="alpha">0.17</div> </div> <div class="knob_group"> <div class="label"><div class="label_text">3d</div></div> <div class="knob" id="x3D" title="x3D">0</div> <div class="knob" id="y3D" title="y3D">0</div> <div class="knob" id="rotateX" title="rotX">0.50</div> <div class="knob" id="rotateY" title="rotY">0.50</div> <div class="knob" id="rotateZ" title="rotZ">0.51</div> <div class="knob" id="zOffset" title="zOffset">0.35</div> <div class="knob" id="zScale" title="zScale">0</div> </div> <div class="knob_group"> <div class="label"><div class="label_text">extra</div></div> <div class="knob" id="posX" title="posX" lfo="">0.3</div> <div class="knob" id="posY" title="posY">0.5</div> <div class="knob" id="sp_t" title="step">0.1</div> <div class="knob" id="anim" title="anim">0.1</div> <div class="knob" id="slow" title="slow">0.1</div> </div> </div>
div, body, input { font-family: Arial, sans-serif; color: white; margin: 0px; } div, body { -moz-user-select: none; -webkit-user-select: none; } body { background: black; } input { -moz-user-select: text; -webkit-user-select: text; background: transparent; } #canvas {width:100%;height:100%;cursor:crosshair;position:fixed} #copyright {position:fixed;left:0px;top:0px;color:gray;font-weight:bold;font-size:12px;margin:5px} #showHide {position:fixed;right:0px;top:0px;color:gray;font-weight:bold;cursor:pointer;margin:5px} #controls {width:400px;height:100%;position:absolute;right:0px;top:50px;display:inline-block;background:transparent} .knob_group {display: inline-block;height: 120px;} .knob { display:inline-block; border: 1px solid gray; border-radius: 3px 3px 3px 3px; height: 111px; padding-top: 0px; width: 43px; } .knob_body { border-radius: 15px 15px 15px 15px; box-shadow: 0 0 5px 1px white; height: 31px; margin: 0px 6px; width: 31px; -moz-user-select: none; -webkit-user-select: none; } .knob_knob { background-color: transparent; cursor: pointer; height: 31px; width: 31px; } .knob_arrow { position:absolute; display:inline-block; top: 15px; width: 15px; height: 1px; background-color: yellow; } .knob_label { diplay: inline-block; font-size: 9px; text-align:center; margin: 2px 0px 6px 0px; } .knob_input { display:inline-block; width: 100%; font-size: 8px; border: 0px; margin: 5px 0px 0px 0px; text-align:center; } .knob_min_input { display:inline-block; width: 18px; font-size: 6px; border: 0px; margin: 0px 0px -10px 2px; text-align:left; color: yellow; } .knob_max_input { display:inline-block; width: 18px; font-size: 6px; border: 0px; margin: 0px 0px -10px 3px; text-align:right; color: yellow; } .knob_lfo_type { margin-top: 2px; font-size: 8px; color: yellow; text-align: center; } .knob_lfo_type_select { // font-size: 8px; // border: 0px; background: black; border: 0px; opacity: 0; height: 10px; position: relative; top: -12px; margin: 3px 2px 0px 2px; text-align:center; } .knob_lfo { display: inline-block; position: relative; top: -12px; } .knob_lfo_body { border-radius: 8px 8px 8px 8px; box-shadow: 0 0 5px 0px white; height: 15px; margin: 3px 3px; width: 15px; -moz-user-select: none; -webkit-user-select: none; background-color: transparent; } .knob_lfo_knob { background-color: transparent; cursor: pointer; height: 15px; width: 15px; } .knob_lfo_arrow { position:absolute; display:inline-block; top: 7px; width: 7px; height: 1px; background-color: yellow; } .knob_lfo_label { diplay: block; font-size: 7px; text-align:center; } .label { display: inline-block; width: 10px; height: 60px; } .label_text { display: inline-block; position: relative; width: 60px; height: 10px; font-size: 9px; -moz-transform: rotate(270deg); -webkit-transform: rotate(270deg); left:-24px; top: -37px; }