Article
A 1993 Windows program, rebuilt for the browser
QuickSmith was written in 1993, for Windows 3.1, in Visual Basic. It was ported to the web around 2011. In 2026 the whole thing was rebuilt underneath without changing how it is used. Here is what that involved and what it turned up.
What it was
The 2011 port worked, and people used it, but it had accumulated the usual
archaeology: jQuery, Bootstrap 3, math.js, Chart.js, bootbox, qTip2,
jquery.contextMenu, a long-click plugin. The Smith chart was drawn on a
canvas. The component symbols were 1-bit .bmp files from 1993,
61 by 20 pixels for a series part and 20 by 70 for a shunt one. The solver
was a direct transcription of the Visual Basic, including a ladder recursion
and a second, separate node-voltage walk for insertion loss.
None of that is a criticism. It is what a program looks like after thirty years of being useful.
Rule one: write the tests first
The first thing built was not code, it was a regression suite. Every case comes from a worked example that already shipped with the program, so the tests encode what QuickSmith is supposed to compute rather than what it currently happens to compute.
The golden values were not captured from the program's own output. They were computed from a separate model of the ladder network written from the element equations, and only then confirmed to agree. Otherwise you are not testing, you are photographing.
That distinction paid for itself immediately, because the suite found four real defects before anything was rewritten:
-
A parallel equivalent calculation used
Qu ^ 2, which in JavaScript is a bitwise XOR rather than a square. It had been wrong since the port. - Insertion loss was referenced to the real part of the load impedance instead of to Z0, so it was not a transducer loss at all.
- Results were stored as sixteen significant digit strings and re-parsed on every read.
- Reflection coefficient was floored at 1e-36, so a perfect match could never report infinite return loss.
And a fifth, the worst: a transmission line was reduced to a lumped series impedance when computing the transfer function. That is correct for Zin and wrong for insertion loss. A lossless 75 ohm quarter wave into a 50 ohm load reported 4.22 dB where the right answer is 0.695 dB, its mismatch loss. The solver now cascades every element as an ABCD matrix, so a lumped part, a stub and a line all go through one code path.
Rule two: the solver knows nothing about the page
engine.js is pure functions over plain data. No DOM, no jQuery,
no external library. It runs on Math plus about forty lines of
complex arithmetic.
There is a test whose only job is to load engine.js and
nothing else at all and solve a few known networks. If the solver
ever reaches back out for the page, that test fails first with a
ReferenceError. It is a cheap way to keep a boundary honest.
Rule three: no build step
It is static files. Open index.html and it runs. There is no
bundler, no transpiler, no package manifest, nothing to install and nothing
to rot. The test runner uses Node if you have it and otherwise falls back to
the JavaScriptCore shell that ships with macOS, so the suite runs on a
machine with no toolchain at all.
For a program that has already outlived two operating systems, that felt like the point.
What went and what arrived
The libraries went. Chart.js was replaced by about 180 lines that draw the
response graph. The dialog plugins were replaced by native
<dialog>. Bootstrap's 121 KB of CSS became a stylesheet
that keeps the class names the markup already had, so it was a swap rather
than a rewrite of every page.
The canvas chart became SVG, which means it is crisp at any zoom, it pans and pinches, and it can be exported as vector art. The component bitmaps became drawn symbols. The four bitmap "matching aid" images became a live line under the chart that names the one or two components that would take you to the centre, updated as you edit.
Then things that were not possible before: drag a node on the chart and the engine solves backwards for the value that puts it under your pointer. Ask the auto-matcher for a particular loaded Q and get Pi and T networks as well as L. Undo and redo over the whole design. A guided tour that rebuilds a worked example one component at a time. Substrate dimensions, so a design comes out as a track width and a length.
The thing that kept happening
One bug shape came up again and again: a colour written into the markup or the JavaScript before the palette existed. Invisible for years, because it happened to match the light theme. Then dark mode arrived and each one lit up.
The wordmark had color:blue inline, which beat the stylesheet.
Nineteen colours in the dialog code were literals. The slot values were set
to black on every redraw. The amplifier page had its own rule
making read-only fields white, which in dark mode gave four white boxes with
pale grey text in them, the four numbers that page exists to report.
A related shape: one token doing two jobs. A single variable was serving as both the panel seam colour and the slot outline colour, so making one look right made the other look wrong. The fix is always the same and always slightly annoying: split the token.
What is still true
It is the same program. You drag a component onto a slot, type a value, and
watch the dot move. The .sch file format is unchanged, so files
saved in 1993 still open. The worked examples are the same examples with the
same numbers, and those numbers are now re-solved on every test run so the
documentation cannot quietly drift away from the code.
There are 50 cases and 174 checks in the main suite, plus separate suites for the chart geometry, the response plot, the component symbols, Touchstone parsing, share links, drag tuning, the guided examples, undo and redo, and the microstrip and coax models.
It is all on GitHub, it is free, and it runs in the browser you already have.