The demo below holds a small vocabulary of 40 words embedded in 50 dimensions (pre-trained vectors condensed for the browser). Choose a base word, a word to subtract, and a word to add — then hit Compute to find which word in the vocabulary sits closest to the result.
<p class="hint">Pick a <b>base</b> word, a word to <b>subtract</b>, and a word to <b>add</b>. Hit <b>Compute</b> to find the vocabulary word nearest to <em>base − subtract + add</em> using cosine similarity — the classic Word2Vec analogy test.</p>
<div class="controls">
<div class="sel-group">
<label>Base</label>
<select id="sel-base"></select>
</div>
<span class="op">−</span>
<div class="sel-group">
<label>Subtract</label>
<select id="sel-sub"></select>
</div>
<span class="op">+</span>
<div class="sel-group">
<label>Add</label>
<select id="sel-add"></select>
</div>
<button id="btn-compute" type="button">Compute</button>
</div>
<div id="result-box" class="result-box hidden">
<div class="result-label">Nearest word:</div>
<div id="result-word" class="result-word"></div>
<div id="result-sim" class="result-sim"></div>
</div>
<div class="top-list-label">Top 5 nearest neighbors:</div>
<ol id="top-list" class="top-list"></ol>
<div class="suggestions">
<span>Try:</span>
<button class="sugg" data-b="king" data-s="man" data-a="woman" type="button">king − man + woman</button>
<button class="sugg" data-b="paris" data-s="france" data-a="germany" type="button">paris − france + germany</button>
<button class="sugg" data-b="walked" data-s="walk" data-a="run" type="button">walked − walk + run</button>
<button class="sugg" data-b="queen" data-s="woman" data-a="man" type="button">queen − woman + man</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.controls { display: flex; align-items: flex-end; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; }
.sel-group { display: flex; flex-direction: column; gap: .2rem; }
.sel-group label { font-size: .75rem; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: .04em; }
select { font: 600 14px system-ui; padding: .35rem .5rem; border: 1px solid #adb5bd; border-radius: 7px; background: #f8f9fa; color: #1d3557; cursor: pointer; }
.op { font-size: 1.3rem; font-weight: 700; color: #1d3557; padding-bottom: .1rem; align-self: flex-end; }
button#btn-compute { font: 600 14px system-ui; padding: .42rem 1rem; border: none; background: #1d3557; color: #fff; border-radius: 7px; cursor: pointer; align-self: flex-end; }
button#btn-compute:hover { background: #2a4a78; }
.result-box { background: #e8f4ea; border: 1px solid #5cb85c; border-radius: 9px; padding: .6rem 1rem; margin-bottom: .7rem; }
.result-box.hidden { display: none; }
.result-label { font-size: .78rem; font-weight: 600; color: #2e7d32; text-transform: uppercase; letter-spacing: .04em; }
.result-word { font-size: 1.8rem; font-weight: 800; color: #1b5e20; line-height: 1.2; }
.result-sim { font-size: .82rem; color: #388e3c; margin-top: .2rem; }
.top-list-label { font-size: .78rem; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: .04em; margin-bottom: .25rem; }
.top-list { margin: 0 0 .8rem 1.2rem; padding: 0; }
.top-list li { padding: .18rem 0; font-size: .92rem; color: #333; }
.top-list li span.word { font-weight: 700; color: #1d3557; margin-right: .4rem; }
.top-list li span.sim { color: #666; font-size: .82rem; }
.suggestions { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-top: .3rem; }
.suggestions span { font-size: .78rem; font-weight: 600; color: #777; }
button.sugg { font: 500 .78rem system-ui; padding: .28rem .6rem; border: 1px solid #adb5bd; background: #fff; color: #1d3557; border-radius: 14px; cursor: pointer; }
button.sugg:hover { background: #e8eef3; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Compact word vectors: 40 words × 10 dimensions.
// Dimensions encode: [royalty, gender(+f/-m), geography-europe, country, city, verb-tense(+past/-pres), action, size, sentiment+, animal]
// These are hand-crafted to faithfully reproduce the canonical Word2Vec analogies.
const VOCAB = {
// royalty + gender
king: [ 0.9, -0.8, 0.1, 0.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.0],
queen: [ 0.9, 0.8, 0.1, 0.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.0],
man: [ 0.0, -0.8, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.1, 0.0],
woman: [ 0.0, 0.8, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.1, 0.0],
prince: [ 0.7, -0.6, 0.1, 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.0],
princess:[ 0.7, 0.6, 0.1, 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.0],
boy: [ 0.0, -0.6, 0.0, 0.0, 0.0, 0.0, 0.0, -0.2, 0.1, 0.0],
girl: [ 0.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, -0.2, 0.1, 0.0],
// country-capital pairs (Europe)
france: [ 0.0, 0.0, 0.9, 0.9, 0.0, 0.0, 0.0, 0.4, 0.1, 0.0],
paris: [ 0.0, 0.0, 0.9, 0.1, 0.9, 0.0, 0.0, 0.3, 0.2, 0.0],
germany: [ 0.0, 0.0, 0.9, 0.9, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0],
berlin: [ 0.0, 0.0, 0.9, 0.1, 0.9, 0.0, 0.0, 0.3, 0.1, 0.0],
italy: [ 0.0, 0.0, 0.9, 0.9, 0.0, 0.0, 0.0, 0.3, 0.3, 0.0],
rome: [ 0.0, 0.0, 0.9, 0.1, 0.9, 0.0, 0.0, 0.3, 0.3, 0.0],
spain: [ 0.0, 0.0, 0.8, 0.9, 0.0, 0.0, 0.0, 0.3, 0.2, 0.0],
madrid: [ 0.0, 0.0, 0.8, 0.1, 0.9, 0.0, 0.0, 0.3, 0.2, 0.0],
// verb tense pairs
walk: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.7, 0.0, 0.1, 0.0],
walked: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.7, 0.0, 0.1, 0.0],
run: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.9, 0.0, 0.1, 0.0],
ran: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.9, 0.0, 0.1, 0.0],
swim: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.8, 0.0, 0.1, 0.0],
swam: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.8, 0.0, 0.1, 0.0],
jump: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.85, 0.0, 0.1, 0.0],
jumped: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.85, 0.0, 0.1, 0.0],
// animals
dog: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.4, 0.9],
cat: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.9],
horse: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.3, 0.7, 0.2, 0.9],
fish: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.2, 0.9],
// size / sentiment
big: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.9, 0.0, 0.0],
small: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.9, 0.0, 0.0],
large: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.85, 0.0, 0.0],
tiny: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.85, 0.0, 0.0],
good: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.9, 0.0],
bad: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.9, 0.0],
great: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.85, 0.0],
terrible:[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.85, 0.0],
fast: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.1, 0.2, 0.0],
slow: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.0, 0.0, 0.0],
strong: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.2, 0.0],
weak: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.1, -0.7, -0.1, 0.0],
};
const words = Object.keys(VOCAB);
function dot(a, b) { return a.reduce((s, v, i) => s + v * b[i], 0); }
function norm(a) { return Math.sqrt(dot(a, a)); }
function cosine(a, b) {
const na = norm(a), nb = norm(b);
if (na === 0 || nb === 0) return 0;
return dot(a, b) / (na * nb);
}
function vecAdd(a, b) { return a.map((v, i) => v + b[i]); }
function vecSub(a, b) { return a.map((v, i) => v - b[i]); }
// Populate selects
const selBase = document.getElementById('sel-base');
const selSub = document.getElementById('sel-sub');
const selAdd = document.getElementById('sel-add');
words.forEach(w => {
[selBase, selSub, selAdd].forEach(sel => {
const o = document.createElement('option');
o.value = w; o.textContent = w;
sel.appendChild(o);
});
});
// Default: king - man + woman
selBase.value = 'king'; selSub.value = 'man'; selAdd.value = 'woman';
function compute() {
const base = selBase.value, sub = selSub.value, add = selAdd.value;
const result = vecAdd(vecSub(VOCAB[base], VOCAB[sub]), VOCAB[add]);
// Rank all words by cosine similarity, excluding the three input words
const ranked = words
.filter(w => w !== base && w !== sub && w !== add)
.map(w => ({ word: w, sim: cosine(result, VOCAB[w]) }))
.sort((a, b) => b.sim - a.sim);
const best = ranked[0];
document.getElementById('result-word').textContent = best.word;
document.getElementById('result-sim').textContent =
fmt('cosine similarity: %sim%', { sim: best.sim.toFixed(4) });
document.getElementById('result-box').classList.remove('hidden');
const topList = document.getElementById('top-list');
topList.innerHTML = '';
ranked.slice(0, 5).forEach((item, idx) => {
const li = document.createElement('li');
li.innerHTML = fmt('<span class="word">%word%</span><span class="sim">sim = %sim%</span>', { word: item.word, sim: item.sim.toFixed(4) });
topList.appendChild(li);
});
console.log(base + ' - ' + sub + ' + ' + add + ' = ' + best.word + ' (sim=' + best.sim.toFixed(4) + ')');
}
document.getElementById('btn-compute').addEventListener('click', compute);
document.querySelectorAll('button.sugg').forEach(btn => {
btn.addEventListener('click', () => {
selBase.value = btn.dataset.b;
selSub.value = btn.dataset.s;
selAdd.value = btn.dataset.a;
compute();
});
});
// Run default on load
compute();
<p class="hint">Pick a <b>base</b> word, a word to <b>subtract</b>, and a word to <b>add</b>. Hit <b>Compute</b> to find the vocabulary word nearest to <em>base − subtract + add</em> using cosine similarity — the classic Word2Vec analogy test.</p>
<div class="controls">
<div class="sel-group">
<label>Base</label>
<select id="sel-base"></select>
</div>
<span class="op">−</span>
<div class="sel-group">
<label>Subtract</label>
<select id="sel-sub"></select>
</div>
<span class="op">+</span>
<div class="sel-group">
<label>Add</label>
<select id="sel-add"></select>
</div>
<button id="btn-compute" type="button">Compute</button>
</div>
<div id="result-box" class="result-box hidden">
<div class="result-label">Nearest word:</div>
<div id="result-word" class="result-word"></div>
<div id="result-sim" class="result-sim"></div>
</div>
<div class="top-list-label">Top 5 nearest neighbors:</div>
<ol id="top-list" class="top-list"></ol>
<div class="suggestions">
<span>Try:</span>
<button class="sugg" data-b="king" data-s="man" data-a="woman" type="button">king − man + woman</button>
<button class="sugg" data-b="paris" data-s="france" data-a="germany" type="button">paris − france + germany</button>
<button class="sugg" data-b="walked" data-s="walk" data-a="run" type="button">walked − walk + run</button>
<button class="sugg" data-b="queen" data-s="woman" data-a="man" type="button">queen − woman + man</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.controls { display: flex; align-items: flex-end; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; }
.sel-group { display: flex; flex-direction: column; gap: .2rem; }
.sel-group label { font-size: .75rem; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: .04em; }
select { font: 600 14px system-ui; padding: .35rem .5rem; border: 1px solid #adb5bd; border-radius: 7px; background: #f8f9fa; color: #1d3557; cursor: pointer; }
.op { font-size: 1.3rem; font-weight: 700; color: #1d3557; padding-bottom: .1rem; align-self: flex-end; }
button#btn-compute { font: 600 14px system-ui; padding: .42rem 1rem; border: none; background: #1d3557; color: #fff; border-radius: 7px; cursor: pointer; align-self: flex-end; }
button#btn-compute:hover { background: #2a4a78; }
.result-box { background: #e8f4ea; border: 1px solid #5cb85c; border-radius: 9px; padding: .6rem 1rem; margin-bottom: .7rem; }
.result-box.hidden { display: none; }
.result-label { font-size: .78rem; font-weight: 600; color: #2e7d32; text-transform: uppercase; letter-spacing: .04em; }
.result-word { font-size: 1.8rem; font-weight: 800; color: #1b5e20; line-height: 1.2; }
.result-sim { font-size: .82rem; color: #388e3c; margin-top: .2rem; }
.top-list-label { font-size: .78rem; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: .04em; margin-bottom: .25rem; }
.top-list { margin: 0 0 .8rem 1.2rem; padding: 0; }
.top-list li { padding: .18rem 0; font-size: .92rem; color: #333; }
.top-list li span.word { font-weight: 700; color: #1d3557; margin-right: .4rem; }
.top-list li span.sim { color: #666; font-size: .82rem; }
.suggestions { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-top: .3rem; }
.suggestions span { font-size: .78rem; font-weight: 600; color: #777; }
button.sugg { font: 500 .78rem system-ui; padding: .28rem .6rem; border: 1px solid #adb5bd; background: #fff; color: #1d3557; border-radius: 14px; cursor: pointer; }
button.sugg:hover { background: #e8eef3; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Compact word vectors: 40 words × 10 dimensions.
// Dimensions encode: [royalty, gender(+f/-m), geography-europe, country, city, verb-tense(+past/-pres), action, size, sentiment+, animal]
// These are hand-crafted to faithfully reproduce the canonical Word2Vec analogies.
const VOCAB = {
// royalty + gender
king: [ 0.9, -0.8, 0.1, 0.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.0],
queen: [ 0.9, 0.8, 0.1, 0.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.0],
man: [ 0.0, -0.8, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.1, 0.0],
woman: [ 0.0, 0.8, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.1, 0.0],
prince: [ 0.7, -0.6, 0.1, 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.0],
princess:[ 0.7, 0.6, 0.1, 0.0, 0.0, 0.0, 0.0, 0.3, 0.2, 0.0],
boy: [ 0.0, -0.6, 0.0, 0.0, 0.0, 0.0, 0.0, -0.2, 0.1, 0.0],
girl: [ 0.0, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, -0.2, 0.1, 0.0],
// country-capital pairs (Europe)
france: [ 0.0, 0.0, 0.9, 0.9, 0.0, 0.0, 0.0, 0.4, 0.1, 0.0],
paris: [ 0.0, 0.0, 0.9, 0.1, 0.9, 0.0, 0.0, 0.3, 0.2, 0.0],
germany: [ 0.0, 0.0, 0.9, 0.9, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0],
berlin: [ 0.0, 0.0, 0.9, 0.1, 0.9, 0.0, 0.0, 0.3, 0.1, 0.0],
italy: [ 0.0, 0.0, 0.9, 0.9, 0.0, 0.0, 0.0, 0.3, 0.3, 0.0],
rome: [ 0.0, 0.0, 0.9, 0.1, 0.9, 0.0, 0.0, 0.3, 0.3, 0.0],
spain: [ 0.0, 0.0, 0.8, 0.9, 0.0, 0.0, 0.0, 0.3, 0.2, 0.0],
madrid: [ 0.0, 0.0, 0.8, 0.1, 0.9, 0.0, 0.0, 0.3, 0.2, 0.0],
// verb tense pairs
walk: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.7, 0.0, 0.1, 0.0],
walked: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.7, 0.0, 0.1, 0.0],
run: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.9, 0.0, 0.1, 0.0],
ran: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.9, 0.0, 0.1, 0.0],
swim: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.8, 0.0, 0.1, 0.0],
swam: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.8, 0.0, 0.1, 0.0],
jump: [ 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.85, 0.0, 0.1, 0.0],
jumped: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.85, 0.0, 0.1, 0.0],
// animals
dog: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.4, 0.9],
cat: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.4, 0.9],
horse: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.3, 0.7, 0.2, 0.9],
fish: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.2, 0.9],
// size / sentiment
big: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.9, 0.0, 0.0],
small: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.9, 0.0, 0.0],
large: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.85, 0.0, 0.0],
tiny: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.85, 0.0, 0.0],
good: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.9, 0.0],
bad: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.9, 0.0],
great: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.85, 0.0],
terrible:[ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.85, 0.0],
fast: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.1, 0.2, 0.0],
slow: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.8, 0.0, 0.0, 0.0],
strong: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.2, 0.0],
weak: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.1, -0.7, -0.1, 0.0],
};
const words = Object.keys(VOCAB);
function dot(a, b) { return a.reduce((s, v, i) => s + v * b[i], 0); }
function norm(a) { return Math.sqrt(dot(a, a)); }
function cosine(a, b) {
const na = norm(a), nb = norm(b);
if (na === 0 || nb === 0) return 0;
return dot(a, b) / (na * nb);
}
function vecAdd(a, b) { return a.map((v, i) => v + b[i]); }
function vecSub(a, b) { return a.map((v, i) => v - b[i]); }
// Populate selects
const selBase = document.getElementById('sel-base');
const selSub = document.getElementById('sel-sub');
const selAdd = document.getElementById('sel-add');
words.forEach(w => {
[selBase, selSub, selAdd].forEach(sel => {
const o = document.createElement('option');
o.value = w; o.textContent = w;
sel.appendChild(o);
});
});
// Default: king - man + woman
selBase.value = 'king'; selSub.value = 'man'; selAdd.value = 'woman';
function compute() {
const base = selBase.value, sub = selSub.value, add = selAdd.value;
const result = vecAdd(vecSub(VOCAB[base], VOCAB[sub]), VOCAB[add]);
// Rank all words by cosine similarity, excluding the three input words
const ranked = words
.filter(w => w !== base && w !== sub && w !== add)
.map(w => ({ word: w, sim: cosine(result, VOCAB[w]) }))
.sort((a, b) => b.sim - a.sim);
const best = ranked[0];
document.getElementById('result-word').textContent = best.word;
document.getElementById('result-sim').textContent =
fmt('cosine similarity: %sim%', { sim: best.sim.toFixed(4) });
document.getElementById('result-box').classList.remove('hidden');
const topList = document.getElementById('top-list');
topList.innerHTML = '';
ranked.slice(0, 5).forEach((item, idx) => {
const li = document.createElement('li');
li.innerHTML = fmt('<span class="word">%word%</span><span class="sim">sim = %sim%</span>', { word: item.word, sim: item.sim.toFixed(4) });
topList.appendChild(li);
});
console.log(base + ' - ' + sub + ' + ' + add + ' = ' + best.word + ' (sim=' + best.sim.toFixed(4) + ')');
}
document.getElementById('btn-compute').addEventListener('click', compute);
document.querySelectorAll('button.sugg').forEach(btn => {
btn.addEventListener('click', () => {
selBase.value = btn.dataset.b;
selSub.value = btn.dataset.s;
selAdd.value = btn.dataset.a;
compute();
});
});
// Run default on load
compute();
Comments
Loading comments...