<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://xenreference.com/wiki/index.php?action=history&amp;feed=atom&amp;title=User%3AInthar%2Fcommon.js</id>
	<title>User:Inthar/common.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://xenreference.com/wiki/index.php?action=history&amp;feed=atom&amp;title=User%3AInthar%2Fcommon.js"/>
	<link rel="alternate" type="text/html" href="https://xenreference.com/wiki/index.php?title=User:Inthar/common.js&amp;action=history"/>
	<updated>2026-06-10T21:56:02Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.44.2</generator>
	<entry>
		<id>https://xenreference.com/wiki/index.php?title=User:Inthar/common.js&amp;diff=7349&amp;oldid=prev</id>
		<title>Inthar at 23:33, 26 May 2026</title>
		<link rel="alternate" type="text/html" href="https://xenreference.com/wiki/index.php?title=User:Inthar/common.js&amp;diff=7349&amp;oldid=prev"/>
		<updated>2026-05-26T23:33:39Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 23:33, 26 May 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l8&quot;&gt;Line 8:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 8:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRES = [&amp;#039;triangle&amp;#039;, &amp;#039;sawtooth&amp;#039;, &amp;#039;square&amp;#039;, &amp;#039;sine&amp;#039;];&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRES = [&amp;#039;triangle&amp;#039;, &amp;#039;sawtooth&amp;#039;, &amp;#039;square&amp;#039;, &amp;#039;sine&amp;#039;];&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRE_LABELS = { triangle: &amp;#039;Triangle&amp;#039;, sawtooth: &amp;#039;Sawtooth&amp;#039;, square: &amp;#039;Square&amp;#039;, sine: &amp;#039;Sine&amp;#039; };&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRE_LABELS = { triangle: &amp;#039;Triangle&amp;#039;, sawtooth: &amp;#039;Sawtooth&amp;#039;, square: &amp;#039;Square&amp;#039;, sine: &amp;#039;Sine&amp;#039; };&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var LOOK_AHEAD = &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;50&lt;/del&gt;.00;    // 50ms buffer so the audio thread can schedule&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var LOOK_AHEAD = &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;0&lt;/ins&gt;.00;    // 50ms buffer so the audio thread can schedule&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var BASE_FREQ = 261.63;   // middle C; pitch is set via detune (cents) on top&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var BASE_FREQ = 261.63;   // middle C; pitch is set via detune (cents) on top&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Inthar</name></author>
	</entry>
	<entry>
		<id>https://xenreference.com/wiki/index.php?title=User:Inthar/common.js&amp;diff=7346&amp;oldid=prev</id>
		<title>Inthar at 22:34, 26 May 2026</title>
		<link rel="alternate" type="text/html" href="https://xenreference.com/wiki/index.php?title=User:Inthar/common.js&amp;diff=7346&amp;oldid=prev"/>
		<updated>2026-05-26T22:34:09Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 22:34, 26 May 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l8&quot;&gt;Line 8:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 8:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRES = [&amp;#039;triangle&amp;#039;, &amp;#039;sawtooth&amp;#039;, &amp;#039;square&amp;#039;, &amp;#039;sine&amp;#039;];&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRES = [&amp;#039;triangle&amp;#039;, &amp;#039;sawtooth&amp;#039;, &amp;#039;square&amp;#039;, &amp;#039;sine&amp;#039;];&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRE_LABELS = { triangle: &amp;#039;Triangle&amp;#039;, sawtooth: &amp;#039;Sawtooth&amp;#039;, square: &amp;#039;Square&amp;#039;, sine: &amp;#039;Sine&amp;#039; };&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var TIMBRE_LABELS = { triangle: &amp;#039;Triangle&amp;#039;, sawtooth: &amp;#039;Sawtooth&amp;#039;, square: &amp;#039;Square&amp;#039;, sine: &amp;#039;Sine&amp;#039; };&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var LOOK_AHEAD = &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;0&lt;/del&gt;.00;    // 50ms buffer so the audio thread can schedule&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var LOOK_AHEAD = &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;50&lt;/ins&gt;.00;    // 50ms buffer so the audio thread can schedule&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var BASE_FREQ = 261.63;   // middle C; pitch is set via detune (cents) on top&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     var BASE_FREQ = 261.63;   // middle C; pitch is set via detune (cents) on top&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Inthar</name></author>
	</entry>
	<entry>
		<id>https://xenreference.com/wiki/index.php?title=User:Inthar/common.js&amp;diff=7338&amp;oldid=prev</id>
		<title>Inthar: Created page with &quot;(function () {     &#039;use strict&#039;;      var ctx = null;     var activeNodes = [];     var activeBtn = null;     var STORAGE_KEY = &#039;edoChordPlayTimbre&#039;;     var TIMBRES = [&#039;triangle&#039;, &#039;sawtooth&#039;, &#039;square&#039;, &#039;sine&#039;];     var TIMBRE_LABELS = { triangle: &#039;Triangle&#039;, sawtooth: &#039;Sawtooth&#039;, square: &#039;Square&#039;, sine: &#039;Sine&#039; };     var LOOK_AHEAD = 0.00;    // 50ms buffer so the audio thread can schedule     var BASE_FREQ = 261.63;   // middle C; pitch is set via detune (cents) on top...&quot;</title>
		<link rel="alternate" type="text/html" href="https://xenreference.com/wiki/index.php?title=User:Inthar/common.js&amp;diff=7338&amp;oldid=prev"/>
		<updated>2026-05-26T20:28:09Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;(function () {     &amp;#039;use strict&amp;#039;;      var ctx = null;     var activeNodes = [];     var activeBtn = null;     var STORAGE_KEY = &amp;#039;edoChordPlayTimbre&amp;#039;;     var TIMBRES = [&amp;#039;triangle&amp;#039;, &amp;#039;sawtooth&amp;#039;, &amp;#039;square&amp;#039;, &amp;#039;sine&amp;#039;];     var TIMBRE_LABELS = { triangle: &amp;#039;Triangle&amp;#039;, sawtooth: &amp;#039;Sawtooth&amp;#039;, square: &amp;#039;Square&amp;#039;, sine: &amp;#039;Sine&amp;#039; };     var LOOK_AHEAD = 0.00;    // 50ms buffer so the audio thread can schedule     var BASE_FREQ = 261.63;   // middle C; pitch is set via detune (cents) on top...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;(function () {&lt;br /&gt;
    &amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    var ctx = null;&lt;br /&gt;
    var activeNodes = [];&lt;br /&gt;
    var activeBtn = null;&lt;br /&gt;
    var STORAGE_KEY = &amp;#039;edoChordPlayTimbre&amp;#039;;&lt;br /&gt;
    var TIMBRES = [&amp;#039;triangle&amp;#039;, &amp;#039;sawtooth&amp;#039;, &amp;#039;square&amp;#039;, &amp;#039;sine&amp;#039;];&lt;br /&gt;
    var TIMBRE_LABELS = { triangle: &amp;#039;Triangle&amp;#039;, sawtooth: &amp;#039;Sawtooth&amp;#039;, square: &amp;#039;Square&amp;#039;, sine: &amp;#039;Sine&amp;#039; };&lt;br /&gt;
    var LOOK_AHEAD = 0.00;    // 50ms buffer so the audio thread can schedule&lt;br /&gt;
    var BASE_FREQ = 261.63;   // middle C; pitch is set via detune (cents) on top&lt;br /&gt;
&lt;br /&gt;
    var timbre = (function () {&lt;br /&gt;
        try {&lt;br /&gt;
            var saved = localStorage.getItem(STORAGE_KEY);&lt;br /&gt;
            return TIMBRES.indexOf(saved) &amp;gt;= 0 ? saved : &amp;#039;triangle&amp;#039;;&lt;br /&gt;
        } catch (e) { return &amp;#039;triangle&amp;#039;; }&lt;br /&gt;
    })();&lt;br /&gt;
&lt;br /&gt;
    function setTimbre(t) {&lt;br /&gt;
        if (TIMBRES.indexOf(t) &amp;lt; 0) return;&lt;br /&gt;
        timbre = t;&lt;br /&gt;
        try { localStorage.setItem(STORAGE_KEY, t); } catch (e) {}&lt;br /&gt;
        document.querySelectorAll(&amp;#039;.edo-chord-timbre&amp;#039;).forEach(function (sel) {&lt;br /&gt;
            sel.value = t;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function getCtx() {&lt;br /&gt;
        if (!ctx) ctx = new (window.AudioContext || window.webkitAudioContext)();&lt;br /&gt;
        return ctx;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function disposeNode(node) {&lt;br /&gt;
        try { node.osc.disconnect(); } catch (e) {}&lt;br /&gt;
        try { node.gain.disconnect(); } catch (e) {}&lt;br /&gt;
        var idx = activeNodes.indexOf(node);&lt;br /&gt;
        if (idx &amp;gt;= 0) activeNodes.splice(idx, 1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function stopAll() {&lt;br /&gt;
        if (!ctx) return;&lt;br /&gt;
        var now = ctx.currentTime + LOOK_AHEAD;&lt;br /&gt;
        // Snapshot and clear before stopping, so onended handlers find no matches and just dispose&lt;br /&gt;
        var toStop = activeNodes;&lt;br /&gt;
        activeNodes = [];&lt;br /&gt;
        toStop.forEach(function (n) {&lt;br /&gt;
            try {&lt;br /&gt;
                n.gain.gain.cancelScheduledValues(now);&lt;br /&gt;
                n.gain.gain.setValueAtTime(n.gain.gain.value, now);&lt;br /&gt;
                n.gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.05);&lt;br /&gt;
                n.osc.stop(now + 0.06);&lt;br /&gt;
            } catch (e) {}&lt;br /&gt;
        });&lt;br /&gt;
        if (activeBtn) { activeBtn.classList.remove(&amp;#039;playing&amp;#039;); activeBtn = null; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function playChord(centsArr, btn) {&lt;br /&gt;
        stopAll();&lt;br /&gt;
        var c = getCtx();&lt;br /&gt;
        if (c.state === &amp;#039;suspended&amp;#039;) c.resume();&lt;br /&gt;
        var now = c.currentTime + LOOK_AHEAD;&lt;br /&gt;
        var attack = 0.02, sustain = 0.2, release = 5;&lt;br /&gt;
        var totalDur = attack + sustain + release;&lt;br /&gt;
        var peak = 0.18 / Math.sqrt(centsArr.length);&lt;br /&gt;
&lt;br /&gt;
        centsArr.forEach(function (cents) {&lt;br /&gt;
            var osc = c.createOscillator();&lt;br /&gt;
            var gain = c.createGain();&lt;br /&gt;
            osc.type = timbre;&lt;br /&gt;
            osc.frequency.value = BASE_FREQ;&lt;br /&gt;
            osc.detune.value = cents;&lt;br /&gt;
            gain.gain.setValueAtTime(0, now);&lt;br /&gt;
            gain.gain.linearRampToValueAtTime(peak, now + attack);&lt;br /&gt;
            gain.gain.setValueAtTime(peak, now + attack + sustain);&lt;br /&gt;
            gain.gain.exponentialRampToValueAtTime(0.0001, now + totalDur);&lt;br /&gt;
            osc.connect(gain).connect(c.destination);&lt;br /&gt;
            osc.start(now);&lt;br /&gt;
            osc.stop(now + totalDur + 0.1);&lt;br /&gt;
&lt;br /&gt;
            var node = { osc: osc, gain: gain };&lt;br /&gt;
            activeNodes.push(node);&lt;br /&gt;
            osc.onended = function () { disposeNode(node); };&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        if (btn) {&lt;br /&gt;
            btn.classList.add(&amp;#039;playing&amp;#039;);&lt;br /&gt;
            activeBtn = btn;&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (activeBtn === btn) { btn.classList.remove(&amp;#039;playing&amp;#039;); activeBtn = null; }&lt;br /&gt;
            }, totalDur * 1000);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectSelectors() {&lt;br /&gt;
        var tables = new Set();&lt;br /&gt;
        document.querySelectorAll(&amp;#039;.edo-chord-play&amp;#039;).forEach(function (btn) {&lt;br /&gt;
            var table = btn.closest(&amp;#039;table&amp;#039;);&lt;br /&gt;
            if (table) tables.add(table);&lt;br /&gt;
        });&lt;br /&gt;
        tables.forEach(function (table) {&lt;br /&gt;
            var firstTh = table.querySelector(&amp;#039;th&amp;#039;);&lt;br /&gt;
            if (!firstTh || firstTh.querySelector(&amp;#039;.edo-chord-timbre&amp;#039;)) return;&lt;br /&gt;
            var sel = document.createElement(&amp;#039;select&amp;#039;);&lt;br /&gt;
            sel.className = &amp;#039;edo-chord-timbre&amp;#039;;&lt;br /&gt;
            sel.title = &amp;#039;Synth timbre&amp;#039;;&lt;br /&gt;
            TIMBRES.forEach(function (t) {&lt;br /&gt;
                var opt = document.createElement(&amp;#039;option&amp;#039;);&lt;br /&gt;
                opt.value = t;&lt;br /&gt;
                opt.textContent = TIMBRE_LABELS[t];&lt;br /&gt;
                if (t === timbre) opt.selected = true;&lt;br /&gt;
                sel.appendChild(opt);&lt;br /&gt;
            });&lt;br /&gt;
            sel.addEventListener(&amp;#039;change&amp;#039;, function () { setTimbre(sel.value); });&lt;br /&gt;
            sel.addEventListener(&amp;#039;click&amp;#039;, function (e) { e.stopPropagation(); });&lt;br /&gt;
            firstTh.innerHTML = &amp;#039;&amp;#039;;&lt;br /&gt;
            firstTh.appendChild(sel);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function handle(e) {&lt;br /&gt;
        var btn = e.target.closest &amp;amp;&amp;amp; e.target.closest(&amp;#039;.edo-chord-play&amp;#039;);&lt;br /&gt;
        if (!btn) return;&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        if (btn === activeBtn) { stopAll(); return; }&lt;br /&gt;
&lt;br /&gt;
        var cents;&lt;br /&gt;
        if (btn.dataset.cents) {&lt;br /&gt;
            cents = btn.dataset.cents.split(&amp;#039;,&amp;#039;).map(Number);&lt;br /&gt;
        } else {&lt;br /&gt;
            var edo = parseInt(btn.dataset.edo, 10);&lt;br /&gt;
            var steps = (btn.dataset.steps || &amp;#039;&amp;#039;).split(&amp;#039;,&amp;#039;).map(Number);&lt;br /&gt;
            if (!edo || !steps.length) return;&lt;br /&gt;
            var stepSize = 1200 / edo;&lt;br /&gt;
            cents = steps.map(function (s) { return s * stepSize; });&lt;br /&gt;
        }&lt;br /&gt;
        if (!cents.length || cents.some(isNaN)) return;&lt;br /&gt;
        playChord(cents, btn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&amp;#039;click&amp;#039;, handle);&lt;br /&gt;
    document.addEventListener(&amp;#039;keydown&amp;#039;, function (e) {&lt;br /&gt;
        if ((e.key === &amp;#039;Enter&amp;#039; || e.key === &amp;#039; &amp;#039;) &amp;amp;&amp;amp; e.target.classList &amp;amp;&amp;amp; e.target.classList.contains(&amp;#039;edo-chord-play&amp;#039;)) {&lt;br /&gt;
            handle(e);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &amp;#039;loading&amp;#039;) {&lt;br /&gt;
        document.addEventListener(&amp;#039;DOMContentLoaded&amp;#039;, injectSelectors);&lt;br /&gt;
    } else {&lt;br /&gt;
        injectSelectors();&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Inthar</name></author>
	</entry>
</feed>