# An electronics lab manual — from Ohm’s law to a blinking 555

Recipe No. 26, Labs and workshops. From The know.sh Cookbook: https://know.sh/cookbook/electronics-lab

Take a class from Ohm’s law to a blinking 555 timer. Each lab lists its parts, describes the circuit pin by pin and ends with a measurement table. Beside the LED lab sits a calculator: pick a resistor’s bands to read its value, or enter a supply and an LED to get the resistor that keeps it alive.

- For: a teacher running an intro electronics class
- You get: five labs from Ohm’s law to a blinking 555, and a resistor calculator
- Time: an evening to draft, two at the bench
- Made with: Shelf, Document, Your AI assistant, Elements, Look up, Public link

## How it works

1. **Draft the labs from your handouts.** Give your assistant your handouts and the datasheets for the parts you stock. Ask for a document per lab, with pin numbers taken only from those datasheets.
2. **Describe every circuit in words.** Name every part, R1 or C2, and write each join as a sentence: pin 8 and pin 4 to the positive rail. Then build the circuit from the words alone.
3. **Put the parts and readings in tables.** Ask for a table element in each parts list and measurement section. Give your own bench readings as a scatter element: the resistor’s straight line beside the LED’s knee.
4. **Ask for a resistor and LED calculator.** Ask for a widget element that reads resistor bands and sizes an LED’s resistor. Check it: 4.7 kΩ is yellow, violet, red, and a 2 V, 20 mA LED on 5 V needs 150 Ω.

## Try this prompt

Your assistant, connected to know.sh (Claude, ChatGPT or a local model):

> In the Parts section of Lab 3 in my know.sh library, add a widget element: a resistor colour-code reader and LED resistor calculator. Draw four or five named bands with a picker for each, and show the value and tolerance; take a typed value and show its bands. For an LED, take supply voltage, forward voltage and current, and give the resistor and the next E12 or E24 value up.

Your assistant, connected to know.sh (Claude, ChatGPT or a local model):

> Using know.sh, make a shelf called Electronics 101 with a document for each lab in my handouts: Ohm’s law, Series and parallel, LEDs, The voltage divider, The 555 timer. Give each lab sections for Parts, The circuit in words, each build step, Measure and Questions, and a Risk section per hazard. Take pin numbers only from the datasheets I have given you.

## Elements in the specimens

Written as your assistant writes them through the know.sh MCP server. Each reads as plain words until you turn elements on (Account → Elements).

Widget, specimen 1:

```element widget
{
  "title": "Resistor colour codes and LED resistors",
  "caption": "Read a four- or five-band resistor by picking its bands, or type a value to get its bands; then work out the current-limiting resistor for an LED, the next standard value up and the power it turns into heat. The page is printed in one ink, so each band is named rather than coloured. Colours follow IEC 60062; only the E12 and E24 series are checked, and the LED sum takes the forward voltage as fixed, which a real LED’s is not quite.",
  "height": 560,
  "html": "<div class=\"res\">\n  <div class=\"tabs\" role=\"tablist\" aria-label=\"Tools\">\n    <button type=\"button\" role=\"tab\" id=\"tabCode\" aria-selected=\"true\" aria-controls=\"panelCode\">Colour code</button>\n    <button type=\"button\" role=\"tab\" id=\"tabLed\" aria-selected=\"false\" aria-controls=\"panelLed\" tabindex=\"-1\">LED resistor</button>\n  </div>\n\n  <section id=\"panelCode\" role=\"tabpanel\" aria-labelledby=\"tabCode\">\n    <div class=\"count\" role=\"group\" aria-label=\"Number of bands\">\n      <button type=\"button\" data-bands=\"4\" aria-pressed=\"true\">4 bands</button>\n      <button type=\"button\" data-bands=\"5\" aria-pressed=\"false\">5 bands</button>\n    </div>\n    <svg id=\"draw\" role=\"img\" aria-label=\"Resistor\"></svg>\n    <div class=\"picks\" id=\"picks\"></div>\n    <dl class=\"figures\">\n      <div><dt>Value</dt><dd id=\"valOut\">–</dd></div>\n      <div><dt>Tolerance</dt><dd id=\"tolOut\">–</dd></div>\n      <div><dt>Could measure</dt><dd id=\"rangeOut\">–</dd></div>\n    </dl>\n    <p class=\"say\" id=\"codeSay\" aria-live=\"polite\"></p>\n    <div class=\"typein\">\n      <label for=\"typed\">Or type a value</label>\n      <div class=\"row\"><input id=\"typed\" type=\"text\" inputmode=\"decimal\" autocomplete=\"off\" spellcheck=\"false\" placeholder=\"4.7k, 4k7, 220, 1M\"><button type=\"button\" id=\"showBands\">Show bands</button></div>\n    </div>\n    <p class=\"say small\" id=\"typeSay\" aria-live=\"polite\"></p>\n  </section>\n\n  <section id=\"panelLed\" role=\"tabpanel\" aria-labelledby=\"tabLed\" hidden>\n    <div class=\"inputs\">\n      <label>Supply voltage, V<input id=\"vs\" type=\"number\" min=\"0\" max=\"24\" step=\"0.1\" value=\"9\"></label>\n      <label>LED forward voltage, V<input id=\"vf\" type=\"number\" min=\"1\" max=\"4\" step=\"0.1\" value=\"2.0\"></label>\n      <label>LED current, mA<input id=\"ma\" type=\"number\" min=\"1\" max=\"50\" step=\"1\" value=\"15\"></label>\n      <label>Standard series<select id=\"series\"><option value=\"E12\" selected>E12</option><option value=\"E24\">E24</option></select></label>\n    </div>\n    <div class=\"presets\" role=\"group\" aria-label=\"Typical forward voltages\">\n      <span>Typical LEDs:</span>\n      <button type=\"button\" data-vf=\"2.0\">Red, 2.0 V</button>\n      <button type=\"button\" data-vf=\"2.1\">Yellow, 2.1 V</button>\n      <button type=\"button\" data-vf=\"3.2\">White or blue, 3.2 V</button>\n    </div>\n    <dl class=\"figures\">\n      <div><dt>Resistor, worked out</dt><dd id=\"rOut\">–</dd></div>\n      <div><dt>Next standard up</dt><dd id=\"stdOut\">–</dd></div>\n      <div><dt>Current with it</dt><dd id=\"iOut\">–</dd></div>\n      <div><dt>Power in the resistor</dt><dd id=\"pOut\">–</dd></div>\n    </dl>\n    <p class=\"say\" id=\"ledSay\" aria-live=\"polite\"></p>\n    <p><button type=\"button\" id=\"toBands\">Read its bands</button></p>\n  </section>\n</div>",
  "css": ".res { font-family: var(--sans); }\n.tabs { display: flex; border-bottom: 1px solid var(--ink); margin-bottom: 12px; }\n.tabs button { flex: 0 0 auto; font: 500 14px/1 var(--sans); background: var(--paper); color: var(--ink-2); border: 0; border-bottom: 3px solid transparent; padding: 10px 14px 8px; min-height: 40px; }\n.tabs button[aria-selected='true'] { color: var(--ink); border-bottom-color: var(--ink); }\n.count { display: flex; gap: 6px; }\n.count button, .presets button, #toBands, .typein button { font: 500 13.5px/1 var(--sans); color: var(--ink); background: var(--paper); border: 1px solid var(--ink); padding: 9px 12px; min-height: 38px; }\n.count button[aria-pressed='true'] { background: var(--ink); color: var(--paper); }\n#draw { display: block; width: 100%; margin: 10px 0 4px; }\n.lead { stroke: var(--ink-2); stroke-width: 2.5; }\n.body { fill: var(--wash); stroke: var(--ink); stroke-width: 1.5; }\n.band { fill: var(--ink); }\n.bname { font: 500 11.5px var(--sans); fill: var(--ink); }\n.bno { font: 400 10.5px var(--sans); fill: var(--ink-3); }\n.tick { stroke: var(--ink-3); stroke-width: 1; }\n.picks { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 8px 12px; }\n.picks select { font-size: 13px; padding: 6px 4px; }\n.picks label { display: grid; gap: 3px; }\n.picks select, .inputs input, .inputs select { width: 100%; min-height: 36px; }\n.figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0 18px; margin: 12px 0 0; border-top: 1px solid var(--ink); }\n.figures div { padding: 6px 0; border-bottom: 1px solid var(--rule); }\n.figures dt { font: 400 12.5px/1.3 var(--sans); color: var(--ink-3); }\n.figures dd { margin: 2px 0 0; font: 400 18px/1.25 var(--serif); color: var(--ink); font-variant-numeric: tabular-nums; }\n.say { margin: 8px 0 0; font: 400 15px/1.45 var(--serif); color: var(--ink); }\n.say.small { color: var(--ink-2); min-height: 1.45em; }\n.typein { margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--rule); }\n.typein label { display: block; margin-bottom: 3px; }\n.row { display: flex; gap: 6px; }\n.row input { flex: 1; min-width: 0; font: 400 15px var(--mono); }\n.inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 8px 12px; align-items: end; }\n.inputs label { display: grid; gap: 3px; }\n.inputs input { font-variant-numeric: tabular-nums; }\n.presets { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; font: 400 13px var(--sans); color: var(--ink-2); }\n#toBands { margin-top: 4px; }\nbutton:focus-visible, input:focus-visible, select:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }",
  "js": "(function () {\n  var $ = function (id) { return document.getElementById(id); };\n  // IEC 60062: digit, multiplier and tolerance for each colour.\n  var C = {\n    black:  { name: 'Black',  d: 0, m: 0 },\n    brown:  { name: 'Brown',  d: 1, m: 1, t: 1 },\n    red:    { name: 'Red',    d: 2, m: 2, t: 2 },\n    orange: { name: 'Orange', d: 3, m: 3 },\n    yellow: { name: 'Yellow', d: 4, m: 4 },\n    green:  { name: 'Green',  d: 5, m: 5, t: 0.5 },\n    blue:   { name: 'Blue',   d: 6, m: 6, t: 0.25 },\n    violet: { name: 'Violet', d: 7, m: 7, t: 0.1 },\n    grey:   { name: 'Grey',   d: 8, m: 8 },\n    white:  { name: 'White',  d: 9, m: 9 },\n    gold:   { name: 'Gold',   m: -1, t: 5 },\n    silver: { name: 'Silver', m: -2, t: 10 }\n  };\n  var DIGITS = ['black', 'brown', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'grey', 'white'];\n  var MULTS = DIGITS.concat(['gold', 'silver']);\n  var TOLS = ['brown', 'red', 'green', 'blue', 'violet', 'gold', 'silver'];\n  var E12 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82];\n  var E24 = [10, 11, 12, 13, 15, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, 51, 56, 62, 68, 75, 82, 91];\n  var RATINGS = [[0.125, '1/8 W'], [0.25, '1/4 W'], [0.5, '1/2 W'], [1, '1 W'], [2, '2 W']];\n  var s = { n: 4, bands: ['yellow', 'violet', 'red', 'gold'] };\n  var multNames = { '-2': '× 0.01', '-1': '× 0.1', '0': '× 1', '1': '× 10', '2': '× 100', '3': '× 1k', '4': '× 10k', '5': '× 100k', '6': '× 1M', '7': '× 10M', '8': '× 100M', '9': '× 1G' };\n\n  function fmt(v) {\n    var u = [[1e9, ' GΩ'], [1e6, ' MΩ'], [1e3, ' kΩ'], [1, ' Ω']], i;\n    for (i = 0; i < u.length; i++) if (v >= u[i][0] * (1 - 1e-9) || i = u.length - 1) return String(parseFloat((v / u[i][0]).toPrecision(4))) + u[i][1];\n  }\n  function roles(n) { return n = 4 ? ['digit', 'digit', 'mult', 'tol'] : ['digit', 'digit', 'digit', 'mult', 'tol']; }\n  function roleLabel(r, i) { return r = 'digit' ? ['1st', '2nd', '3rd'][i] + ' digit' : r = 'mult' ? 'Multiplier' : 'Tolerance'; }\n  function listFor(r, i) { return r = 'digit' ? (i = 0 ? DIGITS.slice(1) : DIGITS) : r === 'mult' ? MULTS : TOLS; }\n  function optText(key, r) { var c = C[key]; return c.name + ' ' + (r = 'digit' ? c.d : r = 'mult' ? multNames[c.m].replace(' ', '') : '±' + c.t + '%'); }\n\n  function read() {\n    var r = roles(s.n), digits = 0, exp = 0, tol = 0;\n    s.bands.forEach(function (b, i) {\n      if (r[i] === 'digit') digits = digits * 10 + C[b].d;\n      else if (r[i] === 'mult') exp = C[b].m;\n      else tol = C[b].t;\n    });\n    return { v: parseFloat((digits * Math.pow(10, exp)).toPrecision(6)), tol: tol, digits: digits, exp: exp };\n  }\n\n  function mantissa(v) { var d = Math.pow(10, Math.floor(Math.log10(v) + 1e-9) - 1); return { m: Math.round(v / d * 1000) / 1000, d: d }; }\n  function inSeries(v, list) { var m = mantissa(v).m; return list.some(function (e) { return Math.abs(e - m) < 1e-6; }); }\n  function neighbours(v, list) {\n    var d = Math.pow(10, Math.floor(Math.log10(v) + 1e-9) - 1), c = [];\n    [d / 10, d, d * 10].forEach(function (k) { list.forEach(function (e) { c.push(parseFloat((e * k).toPrecision(6))); }); });\n    var below = c.filter(function (x) { return x <= v; }).pop(), above = c.filter(function (x) { return x >= v; })[0];\n    return [below, above];\n  }\n  function nextUp(v, list) {\n    var d = Math.pow(10, Math.floor(Math.log10(v) + 1e-9) - 1), c = [];\n    [d, d * 10, d * 100].forEach(function (k) { list.forEach(function (e) { c.push(parseFloat((e * k).toPrecision(6))); }); });\n    return c.filter(function (x) { return x >= v * (1 - 1e-9); })[0];\n  }\n\n  // The fewest significant figures that carry the value: 2 on four bands, 3 on five.\n  function bandsFor(v, n) {\n    var sig = n - 2, lo = Math.pow(10, sig - 1), hi = Math.pow(10, sig) - 1;\n    for (var e = -2; e <= 9; e++) {\n      var x = v / Math.pow(10, e), r = Math.round(x);\n      if (r >= lo && r <= hi && Math.abs(x - r) < 1e-6 * x) {\n        var ds = String(r).split('').map(function (ch) { return DIGITS[Number(ch)]; });\n        return ds.concat([MULTS.filter(function (k) { return C[k].m === e; })[0]]);\n      }\n    }\n    return null;\n  }\n\n  function parse(text) {\n    var t = String(text).replace(/\\s+/g, '').replace(/(Ω|ohms?)$/i, '');\n    var m = /^(\\d*)([rRkKmMgG])(\\d+)$/.exec(t), k = { r: 1, k: 1e3, m: 1e6, g: 1e9 };\n    if (m && (m[1] || m[3])) return parseFloat((m[1] || '0') + '.' + m[3]) * k[m[2].toLowerCase()];\n    m = /^(\\d+(?:\\.\\d+)?|\\.\\d+)([rRkKmMgG]?)$/.exec(t);\n    if (m) return parseFloat(m[1]) * (m[2] ? k[m[2].toLowerCase()] : 1);\n    return null;\n  }\n\n  function buildPicks() {\n    var r = roles(s.n), box = $('picks');\n    box.innerHTML = r.map(function (role, i) {\n      return '<label>Band ' + (i + 1) + ', ' + roleLabel(role, i).toLowerCase() + '<select data-i=\"' + i + '\">' + listFor(role, i).map(function (k) {\n        return '<option value=\"' + k + '\"' + (s.bands[i] === k ? ' selected' : '') + '>' + optText(k, role) + '</option>';\n      }).join('') + '</select></label>';\n    }).join('');\n    box.querySelectorAll('select').forEach(function (sel) {\n      sel.addEventListener('change', function () { s.bands[Number(sel.getAttribute('data-i'))] = sel.value; $('typeSay').textContent = ''; render(); });\n    });\n    document.querySelectorAll('[data-bands]').forEach(function (b) { b.setAttribute('aria-pressed', String(Number(b.getAttribute('data-bands')) === s.n)); });\n  }\n\n  function drawResistor() {\n    var svg = $('draw'), W = Math.max(260, Math.round(svg.getBoundingClientRect().width) || 588), narrow = W < 440;\n    var H = narrow ? 118 : 96, cy = narrow ? 58 : 40, bx = W * 0.14, bw = W * 0.72, bh = 42;\n    var pos = s.n === 4 ? [0.16, 0.3, 0.44, 0.84] : [0.14, 0.26, 0.38, 0.5, 0.86], w = Math.max(9, bw * 0.055), out = [];\n    svg.setAttribute('viewBox', '0 0 ' + W + ' ' + H); svg.setAttribute('height', H);\n    out.push('<line class=\"lead\" x1=\"0\" x2=\"' + bx + '\" y1=\"' + cy + '\" y2=\"' + cy + '\"/><line class=\"lead\" x1=\"' + (bx + bw) + '\" x2=\"' + W + '\" y1=\"' + cy + '\" y2=\"' + cy + '\"/>');\n    var y0 = cy - bh / 2, sh = bw * 0.09;\n    out.push('<path class=\"body\" d=\"M' + bx + ' ' + (y0 + 6) + 'Q' + bx + ' ' + y0 + ' ' + (bx + 8) + ' ' + y0 + 'H' + (bx + sh) + 'L' + (bx + sh + 6) + ' ' + (y0 + 5) + 'H' + (bx + bw - sh - 6) + 'L' + (bx + bw - sh) + ' ' + y0 + 'H' + (bx + bw - 8) + 'Q' + (bx + bw) + ' ' + y0 + ' ' + (bx + bw) + ' ' + (y0 + 6) + 'V' + (y0 + bh - 6) + 'Q' + (bx + bw) + ' ' + (y0 + bh) + ' ' + (bx + bw - 8) + ' ' + (y0 + bh) + 'H' + (bx + bw - sh) + 'L' + (bx + bw - sh - 6) + ' ' + (y0 + bh - 5) + 'H' + (bx + sh + 6) + 'L' + (bx + sh) + ' ' + (y0 + bh) + 'H' + (bx + 8) + 'Q' + bx + ' ' + (y0 + bh) + ' ' + bx + ' ' + (y0 + bh - 6) + 'Z\"/>');\n    s.bands.forEach(function (b, i) {\n      var x = bx + bw * pos[i], inner = x > bx + sh && x < bx + bw - sh, top = inner ? y0 + 5 : y0, bot = inner ? y0 + bh - 5 : y0 + bh;\n      out.push('<rect class=\"band\" x=\"' + (x - w / 2) + '\" y=\"' + top + '\" width=\"' + w + '\" height=\"' + (bot - top) + '\"/>');\n      var up = narrow && i % 2 === 1, ty = up ? y0 - 10 : y0 + bh + 16;\n      out.push('<line class=\"tick\" x1=\"' + x + '\" x2=\"' + x + '\" y1=\"' + (up ? y0 - 6 : y0 + bh + 2) + '\" y2=\"' + (up ? y0 - 1 : y0 + bh + 5) + '\"/>');\n      out.push('<text class=\"bname\" x=\"' + x + '\" y=\"' + ty + '\" text-anchor=\"middle\">' + C[b].name + '</text>');\n      if (!narrow) out.push('<text class=\"bno\" x=\"' + x + '\" y=\"' + (ty + 14) + '\" text-anchor=\"middle\">' + (i + 1) + '</text>');\n    });\n    svg.innerHTML = out.join('');\n    var r = read();\n    svg.setAttribute('aria-label', s.n + '-band resistor, read from the left: ' + s.bands.map(function (b) { return C[b].name.toLowerCase(); }).join(', ') + '. ' + fmt(r.v) + ', plus or minus ' + r.tol + ' per cent.');\n  }\n\n  function render() {\n    drawResistor();\n    var r = read(), names = s.bands.map(function (b) { return C[b].name.toLowerCase(); });\n    $('valOut').textContent = fmt(r.v);\n    $('tolOut').textContent = '±' + r.tol + '%';\n    var lo = fmt(r.v * (1 - r.tol / 100)), hi = fmt(r.v * (1 + r.tol / 100)), ul = lo.split(' ')[1], uh = hi.split(' ')[1];\n    $('rangeOut').textContent = ul === uh ? lo.split(' ')[0] + ' to ' + hi : lo + ' to ' + hi;\n    var series = inSeries(r.v, E12) ? 'It is an E12 value, so also in E24.' : inSeries(r.v, E24) ? 'It is an E24 value, not in E12.' : (function () { var nb = neighbours(r.v, E24); return 'Not an E12 or E24 value (five-band parts often come from E96); the nearest E24 values are ' + fmt(nb[0]) + ' and ' + fmt(nb[1]) + '.'; })();\n    var sentence = names.slice(0, s.n - 2).map(function (n, i) { return C[s.bands[i]].d; }).join('');\n    $('codeSay').textContent = names.join(', ') + ': ' + sentence + ' ' + multNames[r.exp] + ' = ' + fmt(r.v) + ', ±' + r.tol + '%. ' + series;\n  }\n\n  function setBands(v, keepSay) {\n    var four = bandsFor(v, 4), five = bandsFor(v, 5), say = $('typeSay');\n    if (!four && !five) { say.textContent = fmt(v) + ' needs more significant figures than colour bands carry (two on four bands, three on five), or lies outside 0.1 Ω to 999 GΩ.'; return false; }\n    var tol = s.bands[s.bands.length - 1], keep = TOLS.indexOf(tol) >= 0 && s.n === (four ? 4 : 5);\n    if (four) { s.n = 4; s.bands = four.concat([keep ? tol : 'gold']); }\n    else { s.n = 5; s.bands = five.concat([keep ? tol : 'brown']); }\n    buildPicks(); render();\n    if (!keepSay) say.textContent = four && five ? 'On a 1% five-band part the same value reads ' + five.map(function (k) { return C[k].name.toLowerCase(); }).join(', ') + ', brown.' : four ? '' : 'Three significant figures: this value needs five bands.';\n    return true;\n  }\n\n  function typed() {\n    var v = parse($('typed').value);\n    if (v === null || !(v > 0)) { $('typeSay').textContent = 'Type a value such as 4.7k, 4k7, 220, 0.47 or 1M.'; return; }\n    setBands(v);\n  }\n  $('showBands').addEventListener('click', typed);\n  $('typed').addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); typed(); } });\n  document.querySelectorAll('[data-bands]').forEach(function (b) {\n    b.addEventListener('click', function () {\n      var n = Number(b.getAttribute('data-bands')); if (n === s.n) return;\n      var v = read().v, next = bandsFor(v, n);\n      s.n = n;\n      s.bands = next ? next.concat([n = 4 ? 'gold' : 'brown']) : (n = 4 ? ['brown', 'black', 'red', 'gold'] : ['brown', 'black', 'black', 'brown', 'brown']);\n      $('typeSay').textContent = next ? '' : fmt(v) + ' cannot be written on ' + n + ' bands, so they start again at 1 kΩ.';\n      buildPicks(); render();\n    });\n  });\n\n  // Tabs, with arrow keys between them.\n  var tabs = [$('tabCode'), $('tabLed')];\n  function show(i) {\n    tabs.forEach(function (t, j) { t.setAttribute('aria-selected', String(i === j)); t.tabIndex = i === j ? 0 : -1; $(t.getAttribute('aria-controls')).hidden = i ! j; });\n    if (i = 0) render(); else led();\n  }\n  tabs.forEach(function (t, i) {\n    t.addEventListener('click', function () { show(i); });\n    t.addEventListener('keydown', function (e) { if (e.key = 'ArrowRight' || e.key = 'ArrowLeft') { var j = (i + 1) % 2; show(j); tabs[j].focus(); e.preventDefault(); } });\n  });\n\n  // LED: R = (Vs − Vf) ÷ I, then the next standard value up, and what it dissipates.\n  var ledStd = null;\n  function led() {\n    var vs = parseFloat($('vs').value), vf = parseFloat($('vf').value), ma = parseFloat($('ma').value), list = $('series').value === 'E24' ? E24 : E12, say = [];\n    ledStd = null;\n    ['rOut', 'stdOut', 'iOut', 'pOut'].forEach(function (id) { $(id).textContent = '–'; });\n    $('toBands').disabled = true;\n    if (!(vs > 0) || !(vf > 0) || !(ma > 0)) { $('ledSay').textContent = 'Enter the supply voltage, the forward voltage and the current.'; return; }\n    if (vs > 24) { $('ledSay').textContent = 'This calculator stops at 24 V. Keep to low voltage, from batteries or a bench supply, and never work on mains.'; return; }\n    if (vs <= vf) { $('ledSay').textContent = 'The supply has to be above the LED’s forward voltage, or the LED barely lights, if at all. Use a higher supply or fewer LEDs in series.'; return; }\n    var i = ma / 1000, r = (vs - vf) / i, std = nextUp(r, list), ia = (vs - vf) / std, p = ia * ia * std, need = 2 * p;\n    var rating = RATINGS.filter(function (x) { return x[0] >= need; })[0];\n    ledStd = std;\n    $('rOut').textContent = fmt(r);\n    $('stdOut').textContent = fmt(std);\n    $('iOut').textContent = (ia * 1000).toFixed(1) + ' mA';\n    $('pOut').textContent = p < 0.01 ? (p * 1000).toFixed(1) + ' mW' : p.toFixed(3) + ' W';\n    $('toBands').disabled = false;\n    say.push('(' + vs + ' − ' + vf + ') V ÷ ' + ma + ' mA = ' + fmt(r) + '. The next ' + $('series').value + ' value up is ' + fmt(std) + ', which lets ' + (ia * 1000).toFixed(1) + ' mA through: a little under what you asked, never over.');\n    say.push('It turns ' + (p * 1000).toFixed(0) + ' mW into heat (I²R); ' + (rating ? 'with the usual margin of twice that, use a ' + rating[1] + ' resistor or larger.' : 'that is more than a small resistor can take: lower the supply or the current.'));\n    say.push('The LED itself takes ' + (vf * ia * 1000).toFixed(0) + ' mW.');\n    if (vs - vf < 1) say.push('Under a volt across the resistor leaves little headroom: the current will swing with the supply and with each LED.');\n    if (ma > 20) say.push('Above 20 mA: check the LED’s datasheet, as many 5 mm indicator LEDs are rated 20 mA continuous.');\n    $('ledSay').textContent = say.join(' ');\n  }\n  ['vs', 'vf', 'ma', 'series'].forEach(function (id) { $(id).addEventListener('input', led); $(id).addEventListener('change', led); });\n  document.querySelectorAll('[data-vf]').forEach(function (b) { b.addEventListener('click', function () { $('vf').value = b.getAttribute('data-vf'); led(); }); });\n  $('toBands').addEventListener('click', function () {\n    if (!ledStd) return;\n    s.n = 4; s.bands = ['black', 'black', 'black', 'gold'];\n    setBands(ledStd, true);\n    show(0);\n    $('typeSay').textContent = 'The ' + fmt(ledStd) + ' resistor from the LED calculator, with a gold ±5% band, the commonest in a kit.';\n    tabs[0].focus();\n  });\n\n  document.addEventListener('know-tokens', function () { if (!$('panelCode').hidden) render(); });\n  var lastW = 0;\n  if (window.ResizeObserver) new ResizeObserver(function (en) { var w = Math.round(en[0].contentRect.width); if (w !== lastW) { lastW = w; requestAnimationFrame(function () { if (!$('panelCode').hidden) drawResistor(); }); } }).observe($('draw').parentNode);\n\n  buildPicks(); render(); led();\n})();"
}
```

Table, specimen 2:

```element table
{
  "caption": "Parts for the 555 blinker, one kit per pair",
  "columns": [
    {
      "label": "P",
      "align": "left"
    },
    {
      "label": "M",
      "align": "left"
    },
    {
      "label": "W",
      "align": "left"
    }
  ],
  "rows": [
    [
      "U1, NE555 timer",
      "Notch or dot at pin 1",
      "Notch to the left"
    ],
    [
      "R1, 1 kΩ",
      "Brown, black, red, gold",
      "Either"
    ],
    [
      "R2, 10 kΩ",
      "Brown, black, orange, gold",
      "Either"
    ],
    [
      "R3, 470 Ω, for the LED",
      "Yellow, violet, brown, gold",
      "Either"
    ],
    [
      "C1, 100 µF, 16 V or more",
      "Stripe marks negative",
      "Negative to the rail"
    ],
    [
      "C2, 10 nF",
      "103",
      "Either"
    ],
    [
      "LED1, red, 5 mm",
      "Flat edge, short leg",
      "Cathode to the rail"
    ],
    [
      "Supply, 9 V battery",
      "Red lead positive",
      "Connect last"
    ]
  ],
  "notes": "With R1 = 1 kΩ, R2 = 10 kΩ and C1 = 100 µF, the LED is on for about 0.76 s and off for about 0.69 s."
}
```

Scatter, specimen 3:

```element scatter
{
  "caption": "Current against voltage for a 1 kΩ resistor and a red LED",
  "xLabel": "Voltage across the part",
  "xUnits": "V",
  "xMin": 0,
  "xMax": 5,
  "yLabel": "Current",
  "yUnits": "mA",
  "yMin": 0,
  "yMax": 12,
  "points": [
    {
      "x": 1,
      "y": 1.02,
      "group": "1 kΩ resistor"
    },
    {
      "x": 2,
      "y": 2.03,
      "group": "1 kΩ resistor"
    },
    {
      "x": 3,
      "y": 3.05,
      "group": "1 kΩ resistor"
    },
    {
      "x": 4,
      "y": 4.07,
      "group": "1 kΩ resistor"
    },
    {
      "x": 5,
      "y": 5.09,
      "group": "1 kΩ resistor"
    },
    {
      "x": 1.6,
      "y": 0.01,
      "group": "Red LED"
    },
    {
      "x": 1.7,
      "y": 0.2,
      "group": "Red LED"
    },
    {
      "x": 1.8,
      "y": 1.5,
      "group": "Red LED"
    },
    {
      "x": 1.85,
      "y": 3.4,
      "group": "Red LED"
    },
    {
      "x": 1.9,
      "y": 6.2,
      "group": "Red LED"
    },
    {
      "x": 1.95,
      "y": 10.1,
      "label": "Red LED, 10 mA at 1.95 V",
      "group": "Red LED"
    }
  ],
  "source": "Bench readings, 12 September; the resistor measures 983 Ω."
}
```
