index

spike tape

2026·native app · tool design · stage lighting

status — built and tested, preparing for TestFlight. self-initiated.

offline throughout, including the Network screen — it plans a subnet and an address scheme, and never touches a network, sends anything or listens for one. no accounts, no analytics, nothing leaves the device. the App Store privacy label is Data Not Collected.

Barbizon came to City Tech to recruit and handed out their Electrician's Handbook. I still have mine. I put the material back in the order I would have wanted to read it as a beginner and added a few calculators along the way, and noticed a calculator gets to a number faster than reading ever does.

on a ladder, one-handed, in a room with a weak signal or none at all, and often too dim to read comfortably, a printed page was never going to be the fast way there. so those became an app, and it took the name Spike Tape: the tape that marks a fixed point on a stage floor, so nobody has to re-measure it. a checked number does the same job.

none of these sums are hard. they are just easy to get slightly wrong, and a mistake in arithmetic does not crash — it produces a number that looks entirely reasonable and opens a breaker during the show. a printed reference is typed by hand at some point, and a hand-typed table can drift from what the formula actually gives without ever looking wrong. a number computed straight from the formula cannot drift that way — it either runs correctly or it fails outright. reference data has to exist somewhere — a fixture's catalogue angle, a gel's mired rating — and that stays a lookup, clearly marked as one; everything downstream of it is computed fresh every time, not copied from a chart.

what it does

  • load — a mixed rig against a breaker, with the 80% continuous rule applied, and where to split it when it will not fit
  • balance — those circuits dealt across three phases, by whole circuit or by individual fixture
  • address — a DMX start address to DIP switch positions and back, in either offset convention
  • patch — a whole run of fixtures laid out, none of them straddling a universe boundary
  • plan a network — subnets, address plans, and where an sACN or Art-Net universe lands on the wire, entirely on paper
  • correct color — the mired shift between two sources, and the nearest gel that covers it
  • cut gel — pieces per sheet and sheets needed, both orientations
  • size a beam — field and beam diameter at a throw, or solve backward from a target diameter to the angle it needs

those are the five screens. what follows is where the arithmetic stopped being the hard part.

the load screen is the one that had to be right

watts divided by volts is what a lighting handbook teaches, and for a tungsten fixture it is right. LED and discharge fixtures draw more current than their wattage implies, and how much more is a property of the supply rather than something to derive — so a row can be entered in amps straight off the nameplate and mixed with watt rows in the same list. once anything is in amps the totals read VA instead of W, because that is what they have become.

low is the bad direction. a rig that calculates to 15.2 A against a 16 A budget and actually pulls 17 opens the breaker, in the dark, mid-show.

when it will not fit on one circuit it splits it — and does not call that split the fewest possible. it says so only when the packing matches the arithmetic floor of total ÷ usable, where nothing could beat it. the rest of the time it says a split that works, which is a few words away on screen and the honest core of the whole thing.

two ways to balance three phases

balancing turned out to be two different questions, not one. by circuit deals whole circuits onto phases — right when the circuits are already run, since nothing about a live rig can be split after the fact. by fixture deals the individual fixtures first and packs each phase its own circuits — right while the rig is still on paper.

a circuit cannot be split, so dealing by circuit inherits whatever lumpiness the circuits already have: six 5 A fixtures pack into two 15 A circuits, and two circuits cannot cover three phases, so one phase sits at zero. dealing by fixture instead lands 10 A on every phase — at the cost of a third circuit, and the screen only says so when a cost was actually paid.

the same six 5 A fixtures, dealt by circuit — two circuits of three, and L3 carries nothing
the same six 5 A fixtures, dealt by circuit — two circuits of three, and L3 carries nothing
dealt by fixture instead — 10 A level on every phase, at the cost of a third circuit
dealt by fixture instead — 10 A level on every phase, at the cost of a third circuit

the neutral is the one number it will not give you

all three phases are drawn against a single scale, because three meters each with their own scale would make an imbalance invisible.

then there is the neutral, and the neutral is where a correct formula produces a wrong answer. the vector sum of three phase currents is right for linear loads, and stage lighting is not one. phase-controlled dimmers and LED supplies draw in pulses, and the third-harmonic components of all three phases are in phase with one another, so they add in the neutral where the fundamentals cancel. a perfectly balanced rig of the wrong kind of fixture can put current down its neutral that rivals a phase.

so the figure is labelled neutral · at least, with the reason directly underneath it and inside the copied text, where a tidy-up cannot separate the two. an actual harmonic figure would need each fixture's current waveform at each dimmer level, which appears on no spec sheet — and a plausible invented number is the precise thing this app exists not to produce.

both conventions, because the fixtures disagree

a fixture's DIP switches are read one of two ways. on some, the switch total is the address, so switch 1 on its own is address 1. on others the address is the total plus one, so every switch down is address 1. nothing on the fixture announces which, and the same address is a different pattern under each, so a silent default would set the wrong address convincingly.

address 128, counted as the switch total — switch 8 alone. highest reachable this way: 511
address 128, counted as the switch total — switch 8 alone. highest reachable this way: 511
the same 128, counted as the total plus one — switches 1 through 7. highest reachable this way: 512
the same 128, counted as the total plus one — switches 1 through 7. highest reachable this way: 512

both were confirmed against manufacturer documentation rather than assumed: a generic LED PAR 64 counts the switch total, and a Color Kinetics iColor Cove NXT prints a column that is the address minus one. it is genuinely per fixture, which is what earns the control its place on the screen instead of a default buried in the code.

the switch count is not a second choice, on purpose. nine switches address the whole universe — 2⁹ is 512 — so a fixture with a tenth or twelfth switch is not offering a second addressing scheme with it. what those extra switches set is something else entirely: response speed, lamp-on-at-power-up, behavior on a lost signal, documented under their own heading in the fixture's manual. the offset convention above genuinely varies by fixture and stays a control; the switch count does not vary, so it is not one.

laying out a run has a step that gets dropped. there is no channel 513, so a fixture that would not finish inside this universe moves whole to address 1 of the next, and everything after it shifts. the count of how many fit is taken from the address actually typed rather than from 1 — a run starting at 400 has 113 channels in front of it, and zero is a real answer, meaning the first fixture will not go where it was asked to.

the mask is labelled the way the gear asks for it

a subnet mask is a prefix length to the arithmetic — /8, /16, /24 — and a dotted quad to a fixture's own setup menu, which is what actually gets typed in on the day. the screen picks the second form, 255.0.0.0 rather than /8, and draws the boundary between network and host as a ruler alongside it rather than asking anyone to convert between the two by hand.

it also does not invent a range it cannot justify. sACN specifies only its multicast group, so there is no default scheme for it the way Art-Net's 2.x and 10.x ranges have one — the honest answer there is Custom, not a plausible-looking guess.

Art-Net's 2.x range — the ruler marks the boundary the mask sets, and the address plan below fills from the first usable host
Art-Net's 2.x range — the ruler marks the boundary the mask sets, and the address plan below fills from the first usable host

it says how far off the nearest gel is

the shift from 3200K to 5600K works out to −134 mired. Full CTB, the nearest gel in stock, is rated at −131 — the screen prints off by 2.9 rather than rounding that away or presenting the match as if it were exact, because a real stock list rarely lands a gel precisely on the number a formula gives.

Full CTB against the −134 mired shift the correction actually needs
Full CTB against the −134 mired shift the correction actually needs

the cut it will not optimize

gel cutting is a sheet size, a frame size, and how many pieces come out of one. it lays them on a grid rather than searching for a better packing, because a straight blade against a straightedge cannot execute anything else, and a layout nobody can cut is worse than an honest number.

both orientations are shown and neither is chosen. color has no grain, but silk and the directional diffusions scatter along one axis, so turning a piece turns the spread with it. an optimizer that rotated for yield would quietly change what the light does.

which end of the triangle you already have

throw, angle and beam diameter are three sides of one relationship, and which one you are missing depends on the job. sizing a fixture you already own starts from its angle and asks how big the pool will be. covering a mark on a plot starts from the diameter you need and asks what angle gets there.

so the screen asks first — start from fixture, or from target. solving backward from a target diameter inverts the same formula, then brackets the exact answer with the nearest catalogue preset on each side, rather than picking one silently: one wider, which leaves no dark edge but may spill past the mark, and one narrower, which will not spill but may fall short. each side shows the diameter it actually produces, because a 2° gap between two presets is a foot of difference at some throws and inches at others, and the angle alone does not say which.

a 10 ft target at a 20 ft throw needs 41.1° exactly. bracketed by 50° (12.43 ft, would spill) and 36° (8.66 ft, would fall short)
a 10 ft target at a 20 ft throw needs 41.1° exactly. bracketed by 50° (12.43 ft, would spill) and 36° (8.66 ft, would fall short)

nothing survives a relaunch, on purpose

the app keeps no history and asks for no account, so nothing about a rig is still sitting there tomorrow unless it was copied out by hand. that is a constraint, not a missing feature: a rig from a different job silently surviving a relaunch, still reading as current on screen, is exactly the failure this app exists to prevent — a number that is correct and describes nothing true about the room you are standing in now.

the one exception is deliberate too. every screen's nameplate carries a reset control, gated behind a confirmation, because a cleared screen cannot be undone. it is scoped per screen rather than global — a network plan and a rig on Load are unrelated jobs, and clearing one should not touch the other.

a version that can be checked without a phone

the same calculators run as a web version — no build step, no requests, nothing stored — so the arithmetic can be checked by someone who does not carry an iPhone.

porting it turned up one thing worth writing down. the app rounds half to even, so 312.5 displays as 312. JavaScript does neither by default: Math.round breaks ties upward and toFixed rounds away from zero. a direct port would have disagreed with the app on exactly the values that produce a tie, which is the kind of difference nobody finds by looking.

so the page opens on a panel of checks rather than on the calculators. each row feeds a known input to the same code the page runs, and prints what came back beside the value worked out by hand — both columns, side by side. a panel that printed only pass would be the page grading its own paper.

tools

Swift, SwiftUI, Swift Testing, Xcode, JavaScript