Recent projects
Quick References
Field work
MLE Field Tools — sign in
Why this asks
MLE Field Tools is the firm's, and so are the calculations in it. Signing in is the same Microsoft account that opens your email, with the same MFA — there is no second password to remember, and losing access to email loses access here in the same action.
Once you are in, a session lasts 30 days without needing to see the office. If it runs out while you are on a dock, everything keeps working — the calculators, your plans and your notes. Only syncing pauses until you are back in range. You are never locked out of work you already have.
A first sign-in has to be approved before it opens anything. That is not a second identity check — Microsoft has already done that against a mailbox the firm provisioned — it is a deliberate admission, and a record that one was made.
Document viewer
Camera
Take photo reuses the camera for this long, so the phone does not ask for camera access again. The camera indicator stays on while it is held. Leaving the app always releases it.
Toolbar automatic deselect
Each tool checked here returns to Navigate once a mark is finished. Select, Text, Callout and Snapshot always do. Measure waits while Chain is on. Shapes covers line, arrow, rectangle and ellipse.
Mouse wheel
On sheets and photos. Hold Ctrl (Cmd on a Mac) while turning the wheel to do the other; hold Shift to scroll sideways.
Snapping
On sheets and photos, points placed with a shape, Measure, Area or a grip snap to the nearest end or corner of an existing mark; a small blue square shows it. Hold Alt to place freely. Hold Shift to keep a line at 45° steps, or a rectangle or ellipse square.
Keyboard shortcuts
Each tool and view action below can carry one letter or digit: select its field and press the key, or Backspace to clear it. A key belongs to one entry; giving it to another moves it.
Tools
View
Fixed keys
These are the same everywhere and cannot be changed. Cmd stands for Ctrl on a Mac.
Projects on this device
Device storage
New project
Import project
Copies a project from its folder on the MLE File Share (A8 - Field App Data), with its comments, markups, photos and plan sets as last synced. Choose the project folder, such as 24-118 Dockside Marina, or a .zip of it.
Project
Edit project
Export
Generated on this device — no signal needed.
Which plan sets
Which visits
Which documents
Whose markups
Tools
Project calculations, saved with the job and written to its folder on the server when you sync. Separate from the quick references on the home screen, which keep nothing.
Tools
Tool
Summary
Design basis
Pipe sizes are read from fixture units on the Hunter's-curve table for the flush type. Marina uses the flush-tank column with 1-1/4 in. raised to 1-1/2 in. Demand in gpm and velocity are shown for information only.
Irrigation, cooling make-up and similar steady flows go in the continuous field — IPC E103.3 adds those in gpm after the fixture-unit conversion, never as fixture units.
Fixture schedule
Enter quantities once — the same schedule drives the domestic water demand and the sanitary drain size.
Domestic water — IPC Appendix E
Cold + hot sums to more than the total by design: IPC Table E103.3(2) note (a) sets each branch at three-fourths of the fixture total, because the cold and hot peaks do not coincide.
Sanitary sewer — IPC Table 710.1(1)
Fixture-by-fixture breakdown
| Fixture | Qty | Cold | Hot | Total | DFU |
|---|
IPC reference tables
Settings
This trusts the plot. A sheet printed “fit to page”, cropped, or reduced from 24×36 to 11×17 is no longer at the scale written on it, and nothing here can tell. Check a known dimension before you rely on an area.
Roof plan
Half the wall area is added to the roof area, per the firm’s sheet and IPC 1106.4 for a single adjacent wall. Two faces counts the wall twice before that halving.
Results
Sizes are the smallest row of the capacity table that carries the flow. A load past the last row is reported rather than given the largest size.
Allowable flow — quick reference
Allowable flow in gpm. Vertical leader, and horizontal storm drains by slope per foot.
| Pipe size | Vertical leader | 1/8" slope | 1/4" slope | 1/2" slope |
|---|
Docs
Every plan set uploaded for this project. A sheet holds comments from every visit at once — the number in each bubble says which one. Upload a revision as its own set. An image, such as a screenshot of a site map, is added as a single sheet at its own size.
Confirm sheet numbers before importing
Sheet index
Revisions
Each revision as it was issued. The sheet index above always opens the current revision of a drawing.
Nothing on the shelf yet. Use + below to take or pick a photo, or open a photo or snapshot on a comment and use Add to quick references.
A shelf of pictures kept within reach while you read the drawings — the panel schedule, a pedestal detail, a nameplate.
+ below takes or picks a photo straight onto the shelf. To keep something already on a comment, open the photo and use Add to quick references; for a window cut out of a drawing, draw a Snapshot on a comment first, then open it and add it the same way.
The shelf belongs to the whole project, not to this sheet or this walk — it stays as you move between sheets and it is still here on the next visit. It appears in neither export.
The small × on the selected thumbnail takes it off the shelf. A photo taken straight onto the shelf has nowhere else to be, so that × deletes it and asks first.
Feet and inches, or a plain number for feet.
Draw a line across a dimension you already know — a grid spacing, a bay, a dimension string. You will be asked what it reads, and the scale is set from that.
Tap a photo to draw on it.
Comments
Every comment in this project, grouped by site visit. Tap a line to open its photos and edit it. The number is the visit — all of one day's comments carry the same one, on the plans and here.
Tap a photo to draw on it.
Air state — dry bulb + one other property
Chart is drawn from the same equations as the numbers above — it is a check on reasonableness, not a reading surface.
Coil / process load
Mix two airstreams
Duct friction
Size it for me
Governing constraint is whichever gives the larger duct. 0.08–0.10 in./100 ft is a common low-pressure design band; tighten it where fan energy or sound matters.
Fittings and components
Loss coefficients are typical values for a first pass. For a design of record use the ASHRAE Duct Fitting Database for the actual geometry.
Pipe friction — water
Valves and fittings
K values are typical. Confirm against the manufacturer's published Cv for balancing valves, control valves, and strainers — those three dominate a branch and vary widely by model.
Size selection
Affinity laws
Fan horsepower
Pump horsepower
Unit conversion
Temperature
Summary
Design basis
Pipe sizes are read from fixture units on the Hunter's-curve table for the flush type. Marina uses the flush-tank column with 1-1/4 in. raised to 1-1/2 in. Demand in gpm and velocity are shown for information only.
Irrigation, cooling make-up and similar steady flows go in the continuous field — IPC E103.3 adds those in gpm after the fixture-unit conversion, never as fixture units.
Saves a plain-text record of this fixture schedule and its calculated water demand and drain sizing — for the project file, not a substitute for the reviewing engineer's calculation package.
Fixture schedule
Enter quantities once — the same schedule drives the domestic water demand and the sanitary drain size.
Domestic water — IPC Appendix E
Cold + hot sums to more than the total by design: IPC Table E103.3(2) note (a) sets each branch at three-fourths of the fixture total, because the cold and hot peaks do not coincide.
Sanitary sewer — IPC Table 710.1(1)
Fixture-by-fixture breakdown
| Fixture | Qty | Cold | Hot | Total | DFU |
|---|
IPC reference tables
Quick references — firm marina/electrical cheat sheet
Each card below is one reference "file." These are for a quick field check, not a design of record — confirm against the NEC edition and any local amendments adopted by the AHJ for the project before using a number from here on a drawing or calc package.
NEC Table 220.120 — Marina shore power receptacle demand factor
Edition note. This table moved. It is Table 220.120 in the 2023 NEC, Table 555.6 in the 2020 NEC, and Table 555.12 in the 2017 NEC. The demand-factor brackets below are identical in all three — only the citation changes. Cite the designation belonging to the edition the AHJ has adopted.
| Number of shore power receptacles | Demand factor |
|---|---|
| 1–4 | 100% |
| 5–8 | 90% |
| 9–14 | 80% |
| 15–30 | 70% |
| 31–40 | 60% |
| 41–50 | 50% |
| 51–70 | 40% |
| ≥71 | 30% |
1. Where a slip has two receptacles at different voltages (e.g. one 30A/125V and one 50A/125-250V), only the receptacle with the larger kVA demand is counted.
2. A pedestal with an individual kWh submeter per slip, calculated per the criteria in the notes to this table, may have its total demand amperes multiplied by 0.9. (This app's Marina Load tool applies this.)
3. If a circuit feeds a boat hoist and shore power for the same slip, only the load with the larger kW demand is counted.
NEC Table 250.66 — Grounding electrode conductor
Sized from the largest ungrounded service-entrance conductor, or the equivalent area for parallel conductors. Service and electrode sizing only — for a branch circuit or a subpanel feeder use Table 250.122.
| Largest ungrounded service-entrance conductor (AWG/kcmil) | Grounding electrode conductor | ||
|---|---|---|---|
| Copper | Al or Cu-clad Al | Copper | Al or Cu-clad Al |
1. 250.66(A): where the GEC is the sole connection to a rod, pipe or plate electrode, it need not be larger than 6 AWG copper or 4 AWG aluminum.
2. 250.66(B): sole connection to a concrete-encased electrode need not be larger than 4 AWG copper.
3. 250.66(C): sole connection to a ground ring need not be larger than the conductor used for the ring (2 AWG minimum per 250.52(A)(4)).
4. Where there are no service-entrance conductors, size from the equivalent size of the largest service-entrance conductor that would be required for the load served.
5. 250.64(A): aluminum and copper-clad aluminum grounding conductors may not be terminated within 18 in. of the earth, nor used where in direct contact with masonry or earth or subject to corrosive conditions. On a marina that rules them out for most electrode runs — use copper.
NEC Table 250.122 — Equipment grounding conductor
Minimum size EGC for grounding raceway and equipment, by the rating or setting of the overcurrent device ahead of the equipment. Read the amps column as not exceeding — a 30 A or 40 A device falls in the 60 A row.
| OCPD rating, not exceeding A | Copper | Al or Cu-clad Al |
|---|
1. 250.122(A): the EGC is never required to be larger than the circuit conductors supplying the equipment.
2. 250.122(B) — the one that bites on dock runs. Where the ungrounded conductors are increased in size for any reason other than the ampacity correction and adjustment of 310.15(B)/(C) — voltage drop on a long pier feeder being the usual one — the EGC must be increased in the same proportion by circular-mil area. Upsizing a feeder for a long run and leaving the ground at the table size is a common plan-review comment.
3. 250.122(F): each raceway or cable of a parallel installation gets a full-size EGC, sized from the OCPD, not divided among the sets.
4. Rows above 3000 A are deliberately omitted — published transcriptions of the aluminum column disagree at 4000/5000/6000 A. Read those from the printed code.
NEC Table 310.16 — Service entrance conductor & conduit legend
Wire sized for THWN copper. Conduit sized for rigid PVC Schedule 40 — resize for a different conduit. Ampacity per NEC Table 310.16 at 75°C, ≤3 current-carrying conductors, 30°C ambient. The grounding electrode column follows Table 250.66 above.
| Label | Grounding electrode | Conductors/conduit | Runs | Min. conduit | Ampacity 75°C | Φ | Voltage range |
|---|
NEC Table 310.16 & 240.4(D) — Branch circuit & feeder legend with equipment ground
Wire sized for THWN copper. Conduit sized for rigid PVC Schedule 40 — resize for a different conduit. A feeder label with * on the plans indicates a neutral is not required. Ampacity is Table 310.16 at 75°C except the small-conductor rows, where 240.4(D) governs (#12 at 20 A, #10 at 30 A). Equipment grounds follow Table 250.122.
| Label | Conductors per conduit | Runs | Min. conduit | Ampacity 75°C | Φ | Voltage range |
|---|
NEC 400.5 — Portable power cable & cord, marina/boatyard feeder legend
Wire sized using NEC 400.5(A)(1)/(A)(2), green insulated ground. All conductors copper; wet-listed, approved for marina use, suitable for continuous submersion. Conduit sized for rigid PVC Schedule 40. A feeder label with * on the plans indicates a neutral is not required.
| Label | Cable | Runs | Min. conduit | Ampacity 75°C | Φ | Voltage range |
|---|
NEC Ch. 9, Table 1 — Conduit & tubing fill
| Number of conductors/cables | Cross-sectional area allowed |
|---|---|
| 1 | 53% |
| 2 | 31% |
| Over 2 | 40% |
Note 1: based on common conditions of proper cabling and alignment where pull length and bend count are within reasonable limits; certain conditions may call for a larger conduit or fewer conductors.
Note 2: pulling three conductors/cables into a raceway can jam if the raceway ID to conductor/cable OD ratio is between 2.8 and 3.2. Four or more can jam too, but the probability is very low.
Quick Calcs — Single/three-phase amperage & voltage multipliers
| 1Φ | (kVA × 1000) ÷ Volts = Amps |
| 3Φ | (kVA × 1000) ÷ (Volts × 1.732) = Amps |
| 208V × 1.732 | 360.256 |
| 480V × 1.732 | 831.36 |
Voltage drop
Enter any two of wire size, distance, and amps — the third is solved for. Enter all three to check the actual drop for a known run.
Marina shore power load
Demand load for a marina/dock feeder or service — NEC Table 220.120 demand factor applied to shore power receptacles, plus general-use receptacle and pedestal lighting adders. (Same table as 555.6 in the 2020 NEC and 555.12 in the 2017 NEC.)
30A/125V receptacle connected load is counted at half its nameplate rating (15A) — see Basis tab for why. Confirm this matches the feeder configuration on this project before using the result.
Capacity around this result
Total demand load (A) for nearby receptacle counts, same math as above — a zoomed-in window of the firm's Marina Load Calculator sensitivity table.
Before you use a number from this app
Basis — Project Portal
What this stores, and where
Projects, plan sets and sheet indexes are held on this device only. There is no server yet, nothing is uploaded, and nothing is backed up. A project that exists only on one phone is one lost phone away from gone — keep the source plan set in the project folder as always.
Storage is IndexedDB. iOS clears an origin's storage after roughly seven days unused unless the app has been added to the home screen. The app requests persistent storage on every launch, which is what exempts it from that; neither Safari nor Chrome prompts for the request — both decide from whether the app is installed, so adding it to the home screen is the part that has to be done by hand. The Device storage card on the Projects tab reports whether the grant is in force, and warns when it is not.
Who wrote a finding
Every record carries an author, from the first walk — authorship was never optional and is not something retrofitted later. Today that author is the name set on this device, because there is no sign-in yet.
When Microsoft sign-in lands, new records will instead carry the signer's Entra object id — a GUID — with the display name cached alongside it for the report. The object id, never the email address: an email can change hands or be changed outright, and a directory account's object id cannot. Records written before sign-in existed keep their device-name author and are never rewritten, because changing the name on somebody's finding after the fact is forging authorship, not tidying data.
Signing in, and what happens when the session runs out
Not built yet. What follows is the rule it will be built to, stated here so it is a decision rather than whatever the code turns out to do.
Not switched on yet. Until the app registration exists, the sign-in screen says so and the Project Portal behaves exactly as it does today: local to this device, no sync, nothing gated. Turning it on is a deliberate edit to one file, not a side effect of an update.
Sign-in is Microsoft — the same account that opens Outlook, with the
same MFA — and only verified maffett-loftis.com members
get in, after an explicit approval. Guests invited to the tenant for a
SharePoint folder do not, even though they sit inside it. Microsoft is
consulted once, to establish who you are; the session that
follows is issued by the firm's own server and lasts
30 days, which is what makes the offline promise
keepable — a fortnight on a dock with no trip to the office does not
end in a locked app.
An expired session pauses sync. It never blocks work. Plans already on the phone stay readable, comments and photos are still written, and everything queues until the next sign-in. Anything else would take a field user's drawings away from them, on a dock, to protect data they are already holding.
The whole app is behind the sign-in, not just Site Visits. An account that has not been approved reaches nothing — no calculators, no references, not this page. That is a firm decision about firm tools rather than a security boundary: these are static files, so the screen hides the interface, it does not stop anyone who has the files from reading them. The project plans and photos are the asset, and those are behind the server either way.
The one thing that does not lock is an expired session, because that belongs to somebody who was approved. Expiry pauses syncing and nothing else — the calculators and the plans already on the phone keep working. Being shut out of a duct calculation on day 31 of a walk, offline, would be a self-inflicted wound.
Access that is withdrawn is the one harsh case: a device told by the server that its access is gone goes read-only and stops trying to send. Its records are kept and stay exportable — a server instruction does not destroy work that was never uploaded.
The MLE File Share, and where an export lands
Exports can go straight to a folder on the firm's NAS instead of through the share sheet. The Direct to server checkbox on the Export card controls it, it is on by default, and the setting is remembered for your account across every project.
The tree is one folder per person, one per project inside that, and five folders inside each project:
- Bookshelf — quick references for the job. Specs, cut sheets, a manufacturer's manual. Mostly read on the phone and put there from a desk.
- Docs — the source PDF of each plan set added to the app.
- Photos — visit photos, with markup rendered
in, in a folder per visit day named
YYYY-MM-DD. One walk's photographs are one folder rather than part of a single flat list. - Exports — punch-list reports and marked-up sets.
- Tools — the project calculations from the Tools tab, in a folder per tool and one JSON file per tab. Each file records what was entered and what the app returned.
The folder is named from your email handle, and the
project folder from its number and name — so a report for job 24-118
lands in afarris / 24-118 Dockside Marina / Exports. The
share is the same one File Station and Windows Explorer already show,
so a file appears where the firm looks for files, with no export step
afterwards.
Which folder is yours is decided by the server, not by this app. It comes from the account behind your session, so a device cannot write into somebody else's folder — by mistake or otherwise.
An upload that fails still gives you the file. If the NAS cannot be reached, the export falls back to the share sheet or a download and says what went wrong. A report that took two minutes to build on a phone is never dropped in favour of a retry.
Syncing. One button on the project screen, both directions. It sends anything this device made that never reached the server, then brings down anything the server has that this device does not. Files it brings down are kept on the device, so they open later with the radio off.
There is deliberately no folder browser in the app. Those folders are how the files are arranged on the NAS; File Station and Windows Explorer are the place to browse them. What the app answers is a different question — whether this project is current on this phone.
Files reach the office when you tap Sync. Comments, markups and the rest of a project's records go on their own whenever there is a connection (see Shared projects below). A project screen that stalls because it wanted the server is the opposite of what this app is for.
What a sync sends. The whole project: its plan-set
PDFs into Docs, its photos with markup rendered in into Photos, and a
machine-readable copy of every record — comments, pins, markups,
sheets — into a hidden .mle folder beside them.
Those two forms exist for different readers and neither can be made from the other. The folders are for people, and a photo there has its markup burned into the image, so the strokes are no longer separate objects. The hidden copy is what the app reads to rebuild a project, and it keeps the markup as markup. That costs some duplicated bytes on the NAS, knowingly.
A sync only sends what actually changed. Nothing is re-uploaded because a sync ran; each thing is re-sent only when what it would contain has moved. A plan set is rewritten when a sheet, comment or markup on that set changes, so drawing on the mechanical set does not rebuild a forty-sheet electrical one. A photo is re-rendered when its own markup changes and — for a snapshot cut off a drawing — when the ink on that drawing changes, because a snapshot resolves the sheet's markup when it is drawn rather than storing it. A second sync with nothing changed sends one file: the record index.
Files it brings back down are only ever the ones this device does not have. A file that was merely downloaded is never sent back up: it carries nothing the server does not already have, so uploading it could only overwrite somebody else's newer change with a stale copy of their own work.
A file that is on this device and has since been removed from the server is left alone and reported, neither deleted here nor put back there. Which of those two is right depends on why it went, and the app cannot know.
Whether the server has a newer copy is judged on size and modified time, as rsync does by default. A change that preserves both would not be noticed. Comparing file contents would mean the server reading every file on the share on every sync, which is not a thing to do behind a button somebody taps on a dock.
Shared projects and live markup
Records sync by themselves. While signed in and
connected, every comment, markup, photo record and sheet is sent to the
server within about a second of being written, and changes made by
anyone else on the same project arrive the same way. Offline, they wait
on the device and go when the connection returns. Plan-set PDFs and
photographs follow their records into the project's hidden
.mle folder; a device still waiting for one keeps asking
until it arrives.
The server decides the order. Each record carries a revision number and the revision its edit started from, and the server takes a change only if it was made on the copy the server holds. A device's own run of edits is one line of history and never collides with itself. A delete always wins over an edit. Each device's clock is ignored.
Conflicts. When two people change the same item before either has synced, the first to reach the server is kept. The other person's change is not thrown away: the project screen shows a Resolve banner and opens Sync conflicts, which lists each field that differs with both versions side by side. Keep mine writes your version over theirs, Discard mine keeps theirs, and where more than one field differs, Merge as selected takes the fields you pick from each. A comment's severity moves with its informational flag, and a pin's position on the sheet moves as one. An item someone else deleted can only be discarded. Until every conflict on a project is settled, the project cannot be exported. Two copies that turn out the same are settled without asking.
Work offline. The account menu's Work offline stops all syncing on this device: nothing is sent or received, the live connection closes, and others no longer see you on the sheet. Everything still saves on the device and waits. Choosing Go back online sends what waited, brings in what others changed, and reports any conflicts. A dot on the account chip shows the switch is on. It saves battery only where the connection would otherwise be kept busy; airplane mode already stops syncing by itself.
A connection that is not getting through. A phone can report a connection that carries nothing - one bar on a dock, or a marina Wi-Fi that wants a login first. After three requests in a row fail, the app stops the live connection's background traffic and tries again after one minute, then two, then every five, instead of every few seconds. Coming back to the app, the network returning, or saving a change each allows one immediate try. The bar above the portal says when this is happening.
Sharing. The project's owner adds people from the
Shared with card on the project screen. Only approved
firm accounts can be added; guests cannot. When the first person is
added, the project's folder on the NAS moves from the owner's folder to
Shared / <project>, with a file listing its members
for anyone browsing the share. When the last person leaves or is
removed, the folder moves back. Access is decided by the server's
member list, never by the folder.
Who is here. In the sheet viewer, the faces in the top bar are the other people with the project open. Tapping one follows their sheet and view until you touch the drawing or tap Stop following. The Laser Pointer tool in the dock shows your pointer on their screens: on a phone or tablet one finger points without panning or drawing (two fingers still pan and zoom), the dot stays a few seconds after you lift, and a short tail follows it. A stroke being drawn appears faintly to others before it is finished. A text box someone else is typing in cannot be opened until they are done, so two people never type into one box. None of this is stored.
Authorship. Every record keeps the person who made it, separately from whoever edited it last, and each stroke of markup on a photograph keeps its own author. On a project with more than one author, the Export card asks Whose markups to include. Your own name is ticked by default. A partial export names its authors in the file name and the document's author line, numbers its comments with no gaps, and leaves the others' marks out of photographs and snapshots too.
Mine only. On a shared project the lasso's strip has a Mine only pill, lit by default. While it is lit the lasso and Select all take only marks you made, on a sheet or stroke by stroke on a photo, so a sweep cannot move or delete a colleague's work. A mark you edited that someone else made is still theirs. Marks made before anyone signed in count as yours. Tapping a single mark still selects it, whoever made it.
Whose markup is shown. The markup button in the viewer's bottom bar steps through all markup and comments, only yours (on a shared project), and none. The Markups list carries every comment on the set beside the marks, in its severity's colour with its note; choosing one opens it.
What changed. A project with changes by others since you last opened it shows a count on the project list, a dot on each changed sheet, and a line at the top of the project screen. In the viewer, a change arriving from someone else is announced briefly at the bottom of the screen; tapping it opens that sheet.
Projects on this device
A project can be added to, or removed from, any device.
Add from the MLE File Share card on All Projects. It lists the projects in your folder on the server that are not on this device. Adding one restores it — comments, photos and markup included, with the markup still editable — and merges rather than overwrites, so a newer version of a comment already on this device is kept. A project keeps the same identity on every device, so findings made on a phone and on an iPad belong to one job rather than two with the same name.
A project that has never been synced has nothing to restore from, and adding it is refused rather than producing an empty shell that looks like the job and holds none of it.
Remove from this device replaces what used to be Delete. It backs the project up, then asks the server what it actually holds and compares it record by record against what is about to be deleted. If anything is missing, or the server cannot be reached, nothing is removed.
That check is not a formality. An earlier version of this button promised the server kept a copy while records had never been uploaded at all, and it destroyed a project's findings. The promise above is only safe because it is verified every time.
The confirmation carries a Sync to the server first toggle, on every time the card is opened and never remembered. Turned off, the removal skips both the backup and the verification and deletes the project's work outright — the confirmation names how many records and unsent files that is, and the typed job number is still required. It is there for a device that is being cleared deliberately, not as a way past a failed sync.
This is a staging area, not the project folder of
record. A report that becomes a deliverable still has to be
filed into the project's own Docs folder. Renaming a
project, here or on the server, does not strand its folder — each one
carries a marker recording which project it belongs to.
Files up to 256 MB are accepted. A marked-up full set can approach that; if one is refused, the app says so and the limit it names is the server's.
Sheet numbers are read, not known
On import, each sheet's name is taken from the best available source, in this order:
- PDF bookmarks — a set published from Bluebeam or Revit carries one per sheet, named deliberately. Nested bookmarks resolve to the deepest entry, so a sheet inside a discipline folder is named after the sheet, not the folder.
- Page labels, where a set defines them as sheet numbers. Usually absent, or just 1, 2, 3 — in which case they parse to nothing and are skipped.
- The title block text — the bottom-right 38 × 32 % of the page, largest text first. This is guesswork and is labelled as such: the block is not always in that corner, the number is not always the largest text, and CAD often emits each character as its own text run so the number never appears as an intact string.
Which source answered is shown against every sheet at import. A scanned or plotted-to-image sheet with no bookmark comes back unnamed rather than guessed at. Nothing is written to storage until the list has been confirmed.
The separator between the letters and the digits is
dropped. Whatever the source wrote — E-1.1,
E 1.1, E1.1 — the sheet commits as
E1.1, so one drawing has one designation across the
index, the report and the export. A hyphen is never inserted either.
Any number can still be typed as you want it in the import review
screen before anything is written.
Sheet geometry and positions
Positions on a sheet are stored as fractions of the page MediaBox in the page's unrotated orientation, together with the page rotation and the sheet revision they were placed against — never as pixels. Pixels do not survive a different device, a different zoom, or a re-plot.
Distances use the page dimensions in points rather than the normalised fractions, because x and y are fractions of different dimensions. Any measured length also requires a per-sheet scale calibration; an uncalibrated sheet reports no measurement rather than a plausible wrong one.
Revisions
Markups stay bound to the sheet revision they were made on. When a newer revision is imported they are not moved automatically — they are reported as sitting on a superseded sheet, to be carried forward deliberately.
Where an export goes
An export downloads, and you file it. Nothing else was ever possible
from a phone: a browser cannot write to a network share, cannot follow
a file:// link, and has no API to open Explorer or the
Files app at a location.
A project used to carry a server location alongside its name — typed once, shown on the project screen, and explained again in a banner over the export buttons. Nothing ever read it. It was removed rather than left as a field that looks like it does something. The file name carries the project, the job number and the visit, which is what a reader needs to file it.
Folders
Plan sets are grouped into folders, one per visit. A new folder is named
for today's date in YYYY-MM-DD form so folders list in date
order; a second folder on the same day gets (2), then
(3). Renaming is free text, but two folders in a project
cannot share a name — compared case-insensitively, because two folders
differing only in capitals is a trap rather than a distinction.
Sheets imported before folders existed appear under Unfiled. Nothing is hidden because a feature arrived later than the data.
Each sheet in the index carries a menu to renumber it, rename it, or delete it. A hand-entered number is recorded as manual rather than as something read from the file.
Importing a revision
Import Revision is a separate act from Import plan set, and the difference is what it declares. A revision states which existing sets it supersedes, what it is called, and the date it was issued. A plain plan set states none of those — so it can never supersede a drawing and never appears as an issue on the visit timeline. The button is hidden until the project has a set to revise.
A plain plan set is Rev 0. The set as issued is the baseline, not "Rev 1", so the first revision declared against it is Rev 1 and the number reads the way the revision block on the sheet does. Sets imported before this rule keep the sequence they were written with.
Sheets match by their designation. E1.1 in the revision supersedes E1.1 in the sets it was declared against, so a changed-pages-only issue revises the sheets it contains and leaves the rest of the set exactly where it was — which is how a construction set already behaves. A full clouded set works the same way and advances every sheet it holds; either PDF can be the one imported.
Matching is scoped to the sets you tick, never to the whole project. A consultant's package that happens to reuse E1.1 is a different drawing, and nothing links the two unless it is declared. The link is followed through chains, so a revision imported against the last revision still sits in one history with the original issue.
A sheet the revision adds joins the issue. A page whose number matches nothing in the sets you ticked is a new drawing, and the import review marks it new with a choice of where it goes: in sheet-number order (the default: E1.3 lands after E1.2, within its own trade where the set has that trade), after a chosen sheet, or at the end of the set. On a revision declared against several sets split by trade, the default is the set holding the sheet's trade. The sheet then appears in the issued set's index marked added, is included when that set is exported as a full marked-up set, and its comments are filed under the issue in the punch list. The position can be changed later from the sheet's menu (Position in set); if the sheet it was placed after is deleted, it falls back to number order. A later revision of an added sheet keeps its position.
The revision's name is free text — "Rev 2", "Addendum 3", "ASI-04" — and ordering is done on a separate sequence number assigned at import, because those names do not sort against each other. One import is one revision however many documents it is split into by trade.
The name and the date can be changed afterwards, from the menu on the revision's row in the Revisions card. The edit applies to every sheet of that revision, across however many documents the import was split into — a revision is one issuance, so half of it under a different name would appear on the timeline as a revision nobody published. The sequence number is not editable: it is what orders the revisions and what decides which sheet supersedes which, so changing a name can never move a finding. Neither is the list of sets a revision supersedes — re-import it to change that.
A revision can also be removed, from the trashcan on the same row. That deletes the document: its sheets and the PDF they came from. Use it when a set was imported as a revision by mistake, or when an issuance is withdrawn.
The findings on it are not deleted — they are moved. Every comment, markup and photo on those sheets, including everything walked after the revision arrived, is rebound to the revision that becomes current for that drawing once this one is gone: the later revision if there is one, the original issue if there is not. Not to the sheet this revision superseded — where something newer exists that plot is superseded too, and a finding parked on it is reachable only by deliberately opening an older revision, which is a finding lost in everything but name. The position is carried across unchanged and flagged, exactly as carrying a finding forward does: a different plot may put the pedestal somewhere else, and a guess would be a moved finding nobody was told about.
Two things the confirmation counts before you commit. A finding on a sheet the revision introduced has no other plot to sit on, so it goes with the document — the only case where anything is lost, and it is named rather than left to happen quietly. And a later revision declared against the one being removed no longer chains back to the original issue, because nobody declared it against the issue and the app will not invent that.
Nothing about a revision moves a finding. Comments and markups stay bound to the sheet and revision they were placed on, and carrying one forward is a deliberate act that records where it came from and leaves the position for you to correct. See Revisions above.
Which revision you are looking at
A sheet always opens at its current revision. Tapping E1.1 in the sheet index means "show me E1.1", and the answer is the newest issue of it — the answer a drawing office gives. An older revision is never reached by accident; it is chosen deliberately.
The viewer carries a Visible revision control in its top bar, next to the visit filter, listing every revision of the sheet on screen with the current one marked. It is on every sheet: every drawing is at a revision, and the set as issued is Rev 0, so the pill always names what is in front of you. On a drawing that has only one revision it is a label rather than a picker — there is nothing to switch to. On a narrow screen it collapses to the revision's number, the same trade the visit filter makes, because the number is the half printed in the revision block.
Switching revision keeps your place on the paper, not your place on the screen. A revision is a re-plot, so the sheet can be a different size in points; what is carried across is the point you were looking at as a fraction of the page and your zoom as a multiple of fit. Where the new sheet cannot show that centre — a smaller re-plot, or a phone in portrait — the view lands at the nearest position that keeps the sheet on screen, and the zoom is kept.
In the sheet index, a drawing something has superseded carries a badge naming its current revision — which is what a tap will open. A drawing nothing has touched carries no badge: one reading "Rev 1" on every row is forty things to read past to find the two that moved.
Revisions are filed in their own Revisions card below the sheet index rather than in it. A revision is its own PDF, so left in the index a project accumulates one- and two-sheet documents until the issued set is buried among them. Nothing is hidden by the split — every index row already resolves to its current revision and says so — and the card is for reaching a revision as a document, the changed-pages set to read or hand on.
Revisions in an exported set
A full marked-up set holds one page per drawing, at its current revision. Issuing two E1.1s — one of them superseded — is how a contractor builds from the wrong plot, so it does not happen. The plan-set picker offers the issued sets only; choosing an issue still yields every drawing in it, revised or not.
That page carries every finding on the drawing, including ones placed on a revision since superseded. A finding that disappeared from a deliverable because the drawings moved underneath it is the same failure as a report pointing at the wrong pedestal.
But a finding placed on an earlier revision cannot have its position vouched for — a re-plot moves what a pin pointed at, which is why carrying one forward flags it for the engineer. So their note says which revision they were placed on and that the position is not verified against this one. On the exported sheet every comment is a PDF comment note at its point, coloured by its severity, with no numbered bubble drawn on the plan. Every comment appears; none of them silently claims a position it has not earned.
A findings-only export keeps every revision that carries a finding — two pages of E1.1 where both were walked. A finding is where it is, and dropping one would contradict the count on the sheet's own index row. Each page is stamped with its revision, and a superseded one says so, because the only other thing telling two plots of E1.1 apart is the revision block on the drawing itself.
The stamp is absent on a drawing with a single revision — a label reading "Rev 1" on every page of an unrevised set is ink over somebody's linework for nothing. The punch list is unaffected either way: it lists every comment by number and sheet regardless of which revision it was placed on.
Where a revision shows in a list of visits
Every list that separates work by visit — the viewer's visit filter, the sheet index's, and the comments log — marks where a revision was issued. In the two pickers each visit is grouped under the revision it was walked after; walks made before any revision sit in no group. In the comments log the same boundary is drawn as a rule between the two walks, carrying the revision's name. A revision issued since the last walk is shown with nothing after it, because "the drawings moved" is the thing that most needs saying.
The pickers run oldest first, and today takes its own place in that order rather than being pinned near the top. Both follow from the markers: a chronology is only readable in one direction, and it is the direction in which a revision's name sits above the walks made after it. A row pinned out of date order would put every marker either side of it on the wrong boundary. The visit number still counts from the earliest walk, so the list now reads 1, 2, 3 downwards. The comments log stays newest first — the recent walk is the one being discussed, and it draws rules rather than groups.
A visit dated the same day as a revision reads as after it. This is a display convention only: what a finding is actually bound to is the sheet revision recorded on the finding itself, not its position in this list.
The revision's name is shown and its date is not. The date is what positions the marker; a reader is placing two walks either side of an issue, not auditing when it went out.
What deleting removes
Deleting a project removes its records, its plan set files and its queued changes from this device, and reports how much was freed. Deleting a single sheet takes its comments, markups and photos with it — as the confirmation says — but keeps the plan set file as long as any other sheet still uses it, and drops the file once the last one is gone.
Both are local. When sync exists, freeing space on one device will never delete a project for anyone else.
How a sheet is drawn
A sheet is drawn in two layers. The whole sheet is rendered once at screen scale and stretched while you pan or pinch, so movement never exposes blank paper; when you stop, the visible area alone is re-rendered at full zoom and faded over the top.
Only the visible region is rasterised because a browser canvas has a hard area limit — about 16 megapixels on iOS, where exceeding it produces a blank sheet and no error. A 36 × 24 sheet at 4 × zoom would be roughly 107 megapixels, so rendering the whole page at working zoom is not possible at all. Where the limit is reached the render is softened rather than dropped.
Zoom is capped at 16 × the fit-to-screen scale. What you see is the PDF as published — no vector data is re-drawn, re-scaled or re-interpreted, so anything measured off a sheet is measured off the plotted geometry.
The thumbnail rail never renders a page at full resolution and never competes with the sheet. Each thumbnail is capped at 260 px on its long edge — a thumbnail is read as a shape, not as a drawing — and the rail's renders run one at a time, standing down entirely while the sheet's own detail layer is still being drawn.
Scrolling: two modes
The viewer's world is a strip of pages, not one page. Continuous stacks every page of the document and a drag scrolls through the lot; one page at a time holds a single page and a swipe — either direction, horizontal or vertical — turns it. Both run on the same transform, so zoom, ink and pins behave identically in each.
A document opens in the mode its pages imply: portrait pages are a specification, a submittal or an O&M manual and open continuous at fit width; landscape pages are a plan and open one at a time at fit page. Fit-width on a landscape 36 × 24 puts it about 390 px across on a phone, which is not readable by anyone. Your own choice overrides the guess and is remembered on that device.
Only the pages the screen touches, plus one either side, hold a rendered canvas — at most four at once, whatever the screen implies. A forty-page strip of full-size sheets is several hundred megabytes of bitmap, and on iOS that is a crash rather than a slowdown.
Full resolution is spent only on what you are looking at, and only once you stop moving. Pages arrive at about half resolution — cheap, and perfectly readable as a page — and nothing is sharpened while the view is in motion. When it settles, the page in focus alone is re-rendered at full resolution and the detail layer goes over it at the actual zoom. Sharpening four pages on every scroll frame is what made the continuous mode drag.
A swipe turns the page only where there is nothing to scroll — in one-page mode, at fit scale, with one finger. Zoomed in, the same drag is someone tracking a conduit run or reading down a riser, and stealing it would lose their place every time.
The bottom bar
Page size and zoom on the left, the page counter in the middle, view controls on the right. It is always visible, and it takes a real row rather than floating over the drawing — so nothing is ever hidden behind it and nothing has to be summoned back.
An earlier version faded it out after a few idle seconds. That was wrong in practice: the controls disappeared precisely while you were reading the sheet and deciding what to do next, which is when you reach for them, so every third action began with a tap that did nothing but bring the bar back. In pan mode a tap on the drawing now does nothing at all, which is what pan mode means.
The counter reads the sheet number first and the page
ordinal second — E1.1 · 12 of 38. Nobody navigates a marina
set by page number. Tapping it opens a field that accepts either: a sheet
designation (case and punctuation ignored, so e101 finds
E1.1) or a page number. The arrows either side of it step one sheet, and
dim at the ends rather than disappearing — a control that vanishes reads
as a fault and says nothing about why.
Those arrows used to float over the drawing as two round bubbles, because there was no bar to put them in. There is one now, so they were both a second set of controls for one action and the pair sitting on top of the sheet.
On a phone the bar sheds its desk controls. Zoom goes first, because pinch does that job better; then actual size, because “the sheet at the size it would be in your hand” is a question asked of a check print at a desk. What stays is the fit toggle, the page arrows and counter, the sheet's scale and the markup switch — nothing that answers where am I, what scale is this or let me see the drawing clean.
And if it still does not fit, the bar
splits into two rows — the fit toggle and the scale move
above, and the page arrows and counter keep the bottom row, because they
are what a thumb reaches for. Whether it fits is
measured, not assumed: the bar is laid out uncapped for
one frame on open and on every rotation, and asked whether it overflows.
Breakpoints cannot answer that question — it depends on the length of the
sheet number, the length of the scale label
(1 1/2" = 1'-0" is half again as wide as
1" = 40'), the platform's font metrics and your own text-size
setting. Two rounds of guessing fitted the phone they were tried on and
not the next one.
Rotate is a view rotation stored on the sheet, never a change to the file. Because every pin, markup and snapshot region is stored in the page's native orientation, rotating moves nothing that was recorded — the positions are re-derived from the new rotation.
The markup can be hidden in one tap, so the drawing underneath can be read clean on a sheet carrying three walks of findings. Nothing is deleted or rebuilt; it is only made invisible.
At a desk: Page Up/Page Down and Home/End to move, +/− to zoom, F fit width, P fit page, 1 actual size, G go to a sheet, M show or hide markup, R quick references, Esc to close. Ctrl+Z undoes and Ctrl+Y (or Ctrl+Shift+Z) redoes, Cmd on a Mac. The arrow keys nudge a selection one screen pixel, ten with Shift; a run of nudges undoes as one. Ctrl+C, X and V copy, cut and paste marks, across sheets too; a paste lands where the pointer is, and right-clicking empty paper offers Paste. Ctrl+D duplicates. Each dock tool also has a key, set under Settings → Document viewer → Keyboard shortcuts: by default N Navigate, V Select, C Comment, G Snapshot, M Length, K Area, U Count, B Pen, H Highlighter, L Line, A Arrow, R Rectangle, O Ellipse, E Eraser, T Text and Q Callout. A key selects the tool without opening its settings, and a tool hidden in the Toolbox is not selected. G, M and R go to a sheet, show or hide markup and open the quick references only while no tool is bound to them.
The wheel zooms about the cursor. Ctrl (or Cmd) and the wheel scrolls the sheet at its current size — down the drawing without changing how big it is — and Shift does the same sideways. Ctrl+wheel used to be treated as a trackpad pinch and zoomed; nothing is lost, because a plain two-finger scroll already zooms.
Measuring
A scale belongs to a sheet, not to the project. One set routinely carries a 1/8 in floor plan, a 1/4 in enlarged plan and a 1 in = 40 ft site plan, so a single project-wide scale would be wrong on two of them. It is asked for the first time you measure on a sheet and stored on that sheet.
Tapping Measure on a sheet with no scale opens the scale list; once a scale is set it measures, and tapping it again reopens the list to change it. Drag across what you want, or tap the two ends — on a boat two deliberate taps beat one long drag across a 36 × 24. Scales run from full size through the architectural series to 1 in = 100 ft.
Calibrate is the honest answer for a sheet that was scaled at plot, which is most of them once a 30 × 42 has been printed to 11 × 17. Draw across a dimension you already know; the app then asks what that dimension reads and sets the scale from your answer. The line stays on screen while you type it, so the number is entered against the thing it describes, and there is no separate confirm step — by then there is nothing left to decide.
Everything reduces to one number — feet per PDF point — and a PDF point is exactly 1/72 in, so the paper side of that is definitional rather than a guess. Nothing depends on how the sheet happens to be zoomed.
Area uses the same sheet scale. Tap each corner of the
outline, then tap the first corner again, double-click the last one, or
press Enter to close it; Esc or another tool abandons an unfinished
outline. The square footage is the shoelace area of the corners in PDF
points (polygonArea in engine.js, checked
against hand-computed squares, an L shape and a triangle in the
validation suite) multiplied by the square of feet per point. Like a
length, it is recomputed from the current scale every time it is drawn
or exported and never stored, so correcting a scale corrects every area
on the sheet. An outline that crosses itself is not a simple polygon and
its shoelace sum is not its area; draw the outline without crossings.
The same caution about plots that are no longer at their printed scale
applies: check a known dimension first.
Picking a different tool cancels whatever the last one was waiting for: a half-placed measurement, an armed calibration, an armed snapshot, and the prompt on the drawing. An instruction that outlives the tool that asked for it is one somebody follows by accident. The revision warning is not a prompt and stays.
A measurement stores only its two points. The length is recomputed from the sheet's current scale every time it is drawn or exported — so correcting a scale that was set wrong fixes every measurement on that sheet at once, and no stored number can quietly disagree with the scale beside it. A sheet with no scale draws the line and says no scale set rather than inventing a figure.
Lengths read in feet and inches to the nearest sixteenth, rounded before
the split so nothing ever prints as 39'-12", and the fraction
is reduced the way it would be written by hand. Measurements are markup:
they carry a visit, obey the visit filter, and are drawn into the
marked-up plan set.
Chain, the check box at the top left while Measure is the tool, makes each measurement's end the start of the next: every tap after the first ends one leg and begins another. Each leg is saved as an ordinary measurement. When the chain ends — another tool is picked, Chain is switched off, or the viewer is closed — one more record is written with every vertex: a dashed line from the first point to the last, labelled with that straight-line distance, and a Total along the legs. Both are recomputed from the vertices and the sheet's scale like any other measurement. The total is the unrounded sum of the legs, rounded once, so it can differ by a sixteenth from adding the rounded leg labels by hand. Moving a leg afterwards does not move the summary; it is its own record and can be moved or deleted on its own.
Marking up a drawing
Ink goes back on the sheet, from a dock on the right edge. It was taken off once, and for a good reason: a mark on a drawing carried no visit, no author in the report and no place in the punch list, so it could not be tracked and could not reach a deliverable. The terms it returns on are what changed. A markup is visit-stamped exactly like a comment — it obeys the visit filter, draws in that walk's colour, counts on the sheet badges, and is drawn into the marked-up PDF.
What it does not get is a line in the punch list, because a stroke has no text to report. Words still go in a comment, which is numbered, explained and reaches the report; ink says this thing, here. For the same reason the sheet dock has no text tool: loose words on a drawing are the thing that could not be tracked.
The dock's first entries are not ink. Select is where it rests. Comment places a pin. Snapshot frames a window of the drawing. Measure reads a length off the sheet. Below them are the same pen, highlighter, shapes, eraser, label and lasso the photo editor has — the same controls and the same saved palette, so the pen you set up on a photograph is the pen you have on the next sheet. The dock collapses to a single arrow when the drawing wants the room.
A label's words are typed in a prompt rather than on the drawing. The photo editor types in place because the picture fills the screen and the label is a legible size; on a 36 × 24 at fit scale the same editor is six pixels tall, and at a zoom where it is readable the thing being labelled is off screen.
Selecting what is on the drawing
Select is also pan. A tap on empty paper pans; a tap on something drawn picks it up. Once something is selected you can drag it, duplicate it, delete it, or — on a line, arrow, rectangle, oval or measurement — drag either end to reshape it. The lasso takes everything wholly inside the loop, so grazing the end of a long dimension does not catch it.
A move, a duplicate and a reshape are real writes, because the marks are already saved. Undo restores the previous geometry rather than deleting and re-adding, so undoing a nudge cannot tombstone something that was drawn an hour ago.
Tapping a snapshot window — one of the boxes on the plan showing where a photo came from — says which visit it belongs to and what its comment reads, and opens that comment or removes the snapshot. Once selected it can be moved or resized; see Snapshot from a drawing.
Make a comment from this is the bridge between the two halves of the app. Scribble on the drawing in the field, select it, and promote it: the app snapshots those marks at print resolution and opens a new comment holding that window. The marks stay on the sheet — the scribble becomes the illustration of the finding, not its casualty.
A bare tap no longer places a comment. It used to, wherever it landed. Now a tap in pan mode shows or hides the controls and only the Comment tool drops a pin — which is the safer default on a dock, where accidental taps are the normal case. A tapped comment is still not written until it has a word or a photo on it.
The pen and the finger
Once a stylus has been seen on the device, the pen draws and a finger always pans — in every tool mode. That is not a limitation: a 36 × 24 has to be panned before anything on it can be reached, and the hand that pans is the one not holding the Pencil. With no stylus ever seen, one finger draws and two pan, so everybody on a phone can still mark up.
This is the opposite of the photo editor, where one finger draws because the picture already fits the screen. Same tools, different hands, and the rule is a single tested function rather than a decision spread across two handlers.
It is also how palm rejection works. While the pen owns a stroke a touch is ignored outright — not read as a pinch, not captured. A hand resting on the glass is a touch pointer, and writing a word rests it repeatedly, so anything less would delete the stroke being written. No timing heuristics, and no rejected-palm strokes to undo afterwards.
Marks save themselves, like comments, and undo works on what is saved. Undo is scoped to the sitting: it will not reach back into an earlier walk's ink. A stroke too short to be deliberate is discarded rather than left as a speck, and a pinch abandons whatever was being drawn — you changed your mind about the gesture.
A tapped comment is not saved until you save it. An accidental tap opens a panel you dismiss rather than leaving a mark on a drawing. An empty comment with no note and no photo is discarded on save for the same reason.
What a markup is, and what it is not
Strokes are stored as vector geometry over the sheet — never drawn into the PDF. The plan set on this device is byte-for-byte the file that was imported, and any export is a new document.
Ink scales with the paper, so a stroke keeps the size it was drawn relative to the sheet. Comment pins do not: they hold a constant size on screen so they stay tappable when the whole sheet is in view.
Pen strokes are simplified before they are stored, keeping every point within about 0.75 pt of the line you drew — below what the eye resolves, and typically 80 % fewer points. A stroke shorter than 2 pt is treated as a resting stylus and discarded rather than left as a speck.
Everything is bound to the sheet revision it was drawn on and does not move when a newer revision is imported.
Site visits, and the number in the bubble
Every comment records the local calendar day it was written on, stored once by the device that was there. Visits are then numbered by position: the earliest day anything was written is visit 1, the next distinct day is visit 2, and so on. Every comment from one walk therefore carries the same number, on the plans and in the log.
The day is local and is stored, not derived on reading. A comment written at 8 pm is already the next day in UTC, so deriving it from the timestamp would file half an evening walk under tomorrow — and would file it differently again for a reviewer in another timezone.
The number is derived rather than counted, because a counter would need a lock two offline phones cannot share. The consequence is that importing an older visit renumbers the ones after it. That is correct: it genuinely was the earlier visit all along.
Visit colours
One walk is one colour, everywhere it appears: the pins on a drawing, ink drawn directly on it, the counters in the sheet index, the badges on the thumbnails, the headers in the log, and the bubbles in the exported PDF. A number on a 28 px bubble is not readable on a 36 × 24 at fit scale; a colour is, which is how a marked-up plan actually gets read across a table.
Eight defaults cycle by visit number, chosen to hold white text, to stay apart for the common kinds of colour blindness, and to sit on a black-on-white plot without reading as linework. They are a default: any visit's colour can be changed by tapping its swatch in the comment log, and the choice is stored on the project so everyone looking at that job sees the same colours.
A colour has to work twice — as ink on white paper and as a fill holding white text — so a candidate is tested for contrast against white and refused below 3:1. A colour too pale to read on a plot is rejected rather than stored, because a stored setting nobody can see is one that looks applied and is not. Choosing a visit's default back again removes the override rather than storing the same value, so a project with no preference keeps following the defaults.
The comment log
The Comments tab is every comment in the project, grouped by visit, one line each with its photos collapsed until the line is opened. This is the structure a report will be generated from, so the screen and the deliverable cannot drift apart.
The coloured swatch on each visit header is also the control that sets that visit's colour — the log is the one screen that lists every visit at once, which is what makes it the right place for it rather than a settings page nobody opens.
A comment does not have to be on a drawing. Add comment records one with no sheet and no position — access, a submittal, something said on site — which previously had nowhere to go.
One comment is open at a time. Turning to another comment — to edit it, duplicate it or move it — closes the one being written first, and closing writes it: a note or a photograph is saved, an untouched draft leaves nothing behind. A gesture on the comment already open is not a turn away from it, so it does not interrupt the panel. The grey bubble marks where a comment being written will land; it belongs to an open editor and never outlives one.
Severity — the menu beside Done — says how serious a comment is: Informational, Moderate, Elevated or Critical. A new comment is Moderate. The severity sets the colour of the comment's bubble on the drawing, in the comment log and on the exported plans, so a sheet covered in bubbles shows at a glance which of them matter most. The bubble's number is still the visit it was found on.
Informational is field data rather than a defect: the meter reading, what the contractor said, a photograph for reference. It is left off the punch list, because that document is a list of items to close out, and it stays on the marked-up plans as part of the record of the walk. The Export card says how many of a visit's comments are informational and how many the punch list will therefore carry. Comments made before severities existed read as Moderate, or as Informational if they had been flagged so.
Holding a bubble opens its menu. Hold one still for about a third of a second and you get Edit and Duplicate. Tapping anywhere else closes the menu without doing anything. A plain tap still opens the comment, and a press that moves before the menu appears is not a hold at all — a 36×24 is panned constantly and a bubble is a small target, so the gesture is deliberately one that cannot happen by accident.
Hold, then drag, to move it. Once the menu is up, keep your finger down and drag: the menu closes and the comment follows your finger, then stays where you lift. Stay still and lift instead, and the menu stays open for a tap. This is the only way to move a comment, so the menu has no Move. A comment anchored to a snapshot cannot be dragged — its bubble sits on the window's corner — so the drag makes the bubble and its window wiggle and the sheet says to move the window instead.
Duplicate hands the copy to your finger. A hollow dashed bubble follows the pointer and the next tap places it — no second hold. Tapping off the paper cancels, and so does Escape; nothing is written until the tap lands. The copy stays on the same sheet, for the same reason a move does.
It copies the note, the sheet and the visit, and not the photographs: a photograph is evidence of one finding at one moment, and the same picture under two findings would say it was taken twice. Duplicate is for the case the words describe — the same defect at the next pedestal.
A comment moved off the equipment it describes would reach a punch list pointing at the wrong thing, which is why moving always starts with a hold and is never something a stray finger can start. The move is undoable, and it can only go somewhere on the same sheet — carrying a finding onto a different drawing is not a nudge.
The bubble's square corner is its point, and that corner sits exactly on the comment's own position. It is what stays put as you zoom, so the bubble labels the thing beside it rather than covering it.
A comment carrying a snapshot cannot be dragged — the bubble wiggles instead. That bubble is drawn at the snapshot window's corner rather than at its own position, so moving it would write something nothing reads. Tapping the window's border tells you which comment it belongs to and opens it. To move the comment, move the window: once it is selected, drag its border to move it or drag a corner to resize it, and the bubble follows. Remove snapshot keeps the comment, leaves its bubble where it is, and lets it be dragged again.
Photos
Photos attach to a comment, so every photo knows the sheet and the point on it that it belongs to, and the log can link back to that location. They are downscaled to 2000 px on the long edge at JPEG quality 0.80, with a thumbnail — enough to read a nameplate if the shot is framed, and about a tenth of the storage.
Re-encoding applies the camera's rotation flag and discards the rest of the EXIF block, including GPS. Location is therefore not recorded, which is the safer of the two defaults; if a walk needs it, that is a deliberate change rather than an oversight.
Take photo opens a camera inside the app, and every shutter tap saves a photo to the comment with no keep-or-retake step; a bad shot is removed from the comment afterwards. The picture is a frame of the camera's live video rather than a still from the phone's Camera app, so it has none of that app's HDR or night processing. It is asked for at 4:3 and at least 2000 px on the long edge, then stored at the same 2000 px as any other photo. Where the camera cannot open inside the app — access refused, or no support in the browser — the phone's own camera is offered instead. Choose photo opens the photo library.
Zoom with the wheel at the lower right, up to 25×: drag it, or tap a value on it to go straight there. Pinching the viewfinder works too. On iOS the wheel drives the camera's own zoom, so the phone picks the lens and keeps autofocusing as it would in the Camera app. Where the camera's own zoom stops short of 25×, the app takes it to its limit and crops the centre of the frame for the rest, as the Camera app does past its longest lens; that part enlarges detail rather than adding it. Where a browser offers no camera zoom at all, the whole zoom is a crop. A web app cannot set focus itself or focus on a tap. The camera button beside the count opens the phone's own camera for a shot that needs tap-to-focus or the flash; that shot ends on the phone's Use Photo screen.
The camera is kept ready after Done for the time set under Settings in the account menu (5 minutes unless changed), so the next Take photo opens without the phone asking for camera access again. A home-screen app on iOS does not reliably remember that permission once the camera is released, and the app cannot suppress the prompt; reusing the open camera is the only way to avoid it. The camera indicator stays on while the camera is held, and leaving the app releases it immediately.
Zoom
The app's own zoom is disabled — pinching or double-tapping the interface scales nothing, because the sheet viewer does its own zooming and a header that scales out from under a drawing reads as a fault. Every control is at 16 px or larger so browser zoom is not needed to operate it.
Status bar
On an iPhone or iPad the installed app has a solid status bar in the header colour, and the app starts below it. A see-through bar let iOS 27 lay a frosted blur over the top of the app that nothing in the page could remove. iOS fixes this choice when the app is added to the home screen, so a copy installed before v2.0.187 keeps the blur until its icon is deleted and the app is added again from Safari.
Export
Two deliverables, both generated on the device with no signal, and the card is one mode at a time — the two files take different inputs, and a control that does nothing to the thing you are about to make is worse than an absent one.
The marked-up plan set always covers every visit. It is not a deliverable about one afternoon — it is the project's current marked-up state, so there is nothing to choose and no visit picker. Each comment is a PDF comment note coloured by its severity, and its visit is named in the note's subject, so a set walked three times still reads as three walks.
A punch report covers the visits you tick. It is the one document that is a list of work, so it is the one that has to be explicit about which walks it accounts for — a close-out list carrying everything still outstanding across three walks, or one afternoon's findings. It defaults to the most recent visit. The list collapses, and its Select all takes every walk in one tap.
When a report covers more than one visit, the letterhead lists the dates rather than giving a range, and the file name names the visits. Three walks with a fortnight's gap are not a fortnight of walking, and a reader deciding whether an item was seen on the visit they care about needs the actual days. The last box cannot be unticked — a report with no visits in it would have no items.
A punch report can also be narrowed by document. The Which documents list names each plan set holding an item in the ticked visits, plus Not on a drawing for comments taken without a sheet. A finding on a revision is filed under the issued set its drawing belongs to. Every document is included by default, including one imported after a choice was made; the last ticked row cannot be unticked. The marked-up plan set is unaffected.
Marked-up plan set (PDF)
Which sheets is a choice, made before the button. With findings — the default — contains only the sheets carrying a comment or a markup for that visit: a marina set is forty sheets and a walk touches six, so issuing all forty makes the reviewer hunt, and hunting is how a finding gets missed. Full set reproduces every page of the chosen plan sets with the markup on the sheets that have any, which is what a contractor building from the file needs. Both options state their sheet count, on their own line, before either is generated. The estimated file size is not shown against the choice — it is not what the choice is about — but an export large enough to trouble a phone still says so before it runs.
A sheet somebody drew on counts as a finding even with no pin on it — leaving it out would issue a plan with the annotation missing. Ink placed on an earlier revision is excluded, the same way a pin on a superseded sheet is flagged rather than believed.
The selection comes from the same function that writes the summary line on the Export card and drives the comment counter on Docs, so what the badge says is on a sheet is exactly what the export puts in the file.
Bubbles, leader lines and annotation markers are drawn in that visit's colour, the same one the pins carry on screen — see Visit colours below.
Ink drawn directly on a sheet is written into the page content, not as an annotation, because an annotation is invisible on paper and in the simpler phone viewers. The cost of that choice is real and worth stating: a mark in the content stream cannot be switched off or replied to by the reviewer, the way a comment annotation can.
Pages are copied, not re-rendered — the page's own content streams, fonts and images come across untouched, so the drawing that comes out is the drawing that went in. Anything that re-rendered would be substituting this app's idea of the sheet for the sealed original.
Each comment becomes a real PDF text annotation at its
pin: selectable, searchable, carrying author and date, and a contractor
can reply to it in Bluebeam or Acrobat. A numbered bubble is
also drawn into the page content, because an annotation is
invisible on paper and in simpler phone viewers — so the printed set
still reads. Photos ride along as embedded file attachments named
V2-E1.1-03-1.jpg, so the attachment panel sorts into the
same order as the report.
Annotation text is written as UTF-16. The usual PDF
string type cannot carry anything outside ASCII, and a field note
routinely does — 90°, 3/4″, ±, an em dash. An earlier version dropped
those characters silently, so 90° bend arrived as
90 bend.
A pin is stored as a fraction from the top-left; PDF user space
counts from the bottom-left, in points, from the MediaBox
origin — which is not always zero. Rotation needs no handling, and that
is the payoff for storing pins in the page's native orientation: an
annotation rectangle is also in unrotated space, so a pin on a
/Rotate 90 sheet lands correctly for free.
Punch list report (Word)
The firm's letterhead is filled, not rebuilt. Its five
horizontal rules are legacy VML shapes and its branding lives in the
document body rather than a header part, so a rebuilt document would
lose both. The .docx is opened as the zip it is, the tokens
in word/document.xml are replaced, photos are added as
media with their relationships, and the zip is written back.
Every token is a single contiguous run, collapsed offline once. Word splits text into runs at arbitrary boundaries, so a find-and-replace for a placeholder split across five runs matches nothing — silently, producing a report with the placeholder still printed on it. A missing token now fails the export rather than shipping.
Unlike the PDF, this includes every comment, including the ones not on a drawing. A note about access or a missing submittal has nothing to mark up but still belongs in the report. Photos are drawn at 4.2 in wide against the letterhead's 6.1 in usable width, aspect preserved, with their markup burned into that copy only — the stored photo stays clean.
Files are handed to the share sheet where the device has one, which on
iOS is the only route to Files, Mail or Teams; elsewhere they download.
pdf-lib (1.17.1) and fflate (0.8.3) are
vendored alongside pdf.js for the same reason — a CDN is not there on a
dock. Only fflate's synchronous API is used: the async half spawns a
worker from a blob: URL, which this app's
worker-src 'self' forbids.
Marking up a photo
Drawing has moved off the drawing and onto the photograph. The pen, eraser, ink colour, line width and undo used to be docked over the sheet viewer; they now live in the photo annotator, reached by tapping any saved photo on a comment.
The reason is trackability. A mark on a sheet carried no visit number, no author in the report and no place in the punch list — it could not be exported and could not be answered. A mark on the photograph says the thing only the photograph can, and it travels attached to the comment that explains it.
Palm rejection, and who is allowed to mark
Once a stylus has been seen on the device, touch stops marking: the pen draws and fingers navigate. That rejects a resting palm for free — a palm is a touch pointer — with no heuristics, no timing windows and no rejected-palm strokes to undo.
The state is latched, not decided per event, because a palm often lands on the glass a moment before the nib and would otherwise start a stroke in the gap. It stays latched once set: someone who has put the Pencil down still does not want their hand drawing.
With no stylus ever seen, the finger draws — most of the firm is on a phone, and refusing touch outright would mean most people could not mark up a photo at all. Pan mode inverts the rule deliberately: the stylus moves the picture and the finger draws, for working without the Pencil in hand.
Apple Pencil's double-tap is not available to this app. It is delivered through UIPencilInteraction, which iPadOS exposes only to native applications — a web app never receives it. The eraser is a palette button.
On a sheet, the stylus now pans and taps to place a comment like any other pointer. It used to be excluded because the pen drew on drawings; nothing draws on a sheet any more, so putting the Pencil down to place every pin was pure friction.
A photo can be marked up while it is being added, before the comment is saved. A photo is taken because of what is wrong with it, and being made to save first and come back afterwards turned one thought into two trips. A photo that has no record yet keeps its markup in memory and carries it onto the record when the comment is saved.
Freehand, and why it is smoothed
Every sample used to be joined to the next with a straight segment. A pointer reports at frame rate, so a handwritten note came out as a chain of short straight runs with a corner at every sample — which reads as the app straightening what was drawn, and is what made field handwriting look wrong.
Strokes are now drawn as a quadratic curve through the midpoints of consecutive samples, each sample acting as the control point. The curve passes within half a sample spacing of every sample — far below what anyone can see — and it removes the corners the sampling introduced rather than the ones the hand made. Simplification is still applied, at a tolerance measured in image pixels and kept fine enough to preserve writing.
One path builder serves the annotator, the thumbnails, the report canvas, the projection onto the sheet and the PDF, so the ink on screen cannot differ from the ink in the deliverable.
The palette
Quick-select colours and line weights are the person's, not the project's — someone who redlines in orange redlines in orange on every job — so they live in browser storage, which is per-device and follows you across every project on it.
Any colour is reachable through the system colour picker: full spectrum, eyedropper, hex entry, sliders, all working offline. That is deliberate rather than a compromise — a hand-built wheel would be more code than the rest of the annotator and worse on a touch screen. Colours you use are remembered as recents, capped at five; + saves the current colour to the palette permanently, and holding a swatch removes it. The palette never empties.
Three line-weight presets, and the slider updates the nearest preset — adjust once and it is there next time, with no separate save step. The palette scrolls rather than growing: a tall iPad has room for a dozen swatches, a phone in landscape does not, and a dock that grew would put its bottom controls past the edge of the glass with nothing to say why.
Marks store a colour as a hex value now. The five names the app shipped with still resolve, and always will — a stroke drawn in “red” in August cannot turn black because the palette changed.
Highlighter, text and select
The highlighter is its own tool with its own colour and weight, drawn beneath the ink so pen strokes stay readable on top of it — which is what a highlighter does. Yellow is an ordinary pen colour again. Draw order is highlight, then geometry, then text, and it is the same in all four renderers, so the report cannot contradict the screen.
Text labels are placed with a tap and typed into a panel rather than an in-place caret — an editable SVG text node on a touch screen is a fight, and a field note is usually two words. They carry a white halo, because a label over a photograph of a panel interior is unreadable without one. Tapping existing text re-opens it; clearing the words deletes it. Text is not projected onto the plan: a label sized for a photograph is illegible at sheet scale, and the note it belongs to is already on the comment.
Select draws a lasso and drags what it caught. A mark is in the loop when all of it is — “any point inside” sounds friendlier and is worse, because a long stroke crossing the loop gets dragged away when you meant the small thing it passes over. Moving is clamped as a whole rather than per point, so a shape reaching the edge of the picture stops instead of deforming. The selection itself is never stored; it is a gesture.
Shapes
A rectangle round a panel, an arrow to the thing that is wrong, an oval round a tag. Drawn freehand these are slow and look untidy; as shapes they are one drag and they read as deliberate.
Every shape is stored as the two points of the drag — any aspect ratio, any length, any direction. A rectangle dragged right-to-left is the same rectangle, because the geometry is derived from the two points rather than stored as an origin plus a size. Every shape renders through one function that turns it into plain polylines, so an oval on the phone and the same oval in the PDF cannot drift apart.
The eraser measures against a shape's outline, never its bounding box: erasing a rectangle by touching its empty middle would take out the thing it was drawn around, and on an oval round a tag the middle is the tag.
Markup is stored as vectors over the image, as fractions of the image width and height — never burned into the JPEG. Burning it in would re-encode the photograph on every edit, make the markup impossible to remove, and cap the report at phone resolution. Stored as vectors, the same numbers drive the thumbnail, the full-size view, and the print-resolution render a report will use.
Strokes live on the photo record rather than as records of their own. A photograph has one author by construction and is edited as a single object behind a Done button, so a forty-stroke markup queues one sync operation instead of forty. Sheet ink keeps its one-record-per-stroke model, because marks on a drawing are independent and may be by different people.
Snapshot from a drawing
Snapshot on a comment lets you drag a box on the sheet and attach that window as a photo. It is what a report usually wants: the detail from the drawing next to the photograph of what is wrong with it.
The selection is made in screen pixels and converted to page points before anything renders, then re-rendered from the PDF at 2.5× the on-screen size. Copying the pixels off the canvas instead would capture the blurry CSS-scaled base layer at phone resolution — fine on the phone that took it, unusable in print. A selection dragged past the edge crops at the paper rather than capturing margin.
A comment carries one snapshot, in its own slot in the comment editor beside the photographs. A comment recorded before this rule keeps every snapshot it already had, on the plan and in the report; drawing a new one replaces the first.
The window a snapshot was taken through is drawn on the sheet as a solid frame — dashed reads as tentative on a plotted sheet, where dashed linework already means something specific. Its colour is the visit colour, the same one the pin, the sheet badges and the log headers use, so a frame says which walk cut the window and cannot disagree with the finding it belongs to.
A snapshot shows the markup drawn on the drawing. Any pen, highlighter, shape or note on the sheet inside the window appears in the snapshot — in the comment strip, in the log, in the punch-list report and in the marked-up PDF. It works both ways: markup on a snapshot is stored as fractions of that window and projects back onto the plan.
Assumption and limitation. The ink is never burned into the snapshot image. The picture stays the sheet rendered from the PDF, and the marks are drawn over it every time it is shown, so a snapshot cannot go out of date — mark the drawing an hour later, move a mark or delete one, and the snapshot follows. The consequence is that a snapshot shows all visits' markup inside its window, not only the walk that took it, because it is reporting what the drawing says now. A snapshot is therefore not editable as a photograph: mark up the drawing instead, where the mark carries a visit and reaches the punch list. A mark that straddles the window's edge is clipped by the frame, not split.
The comment's numbered bubble moves to the top-right corner of its first snapshot, so the number labels the window instead of sitting in the middle of the detail the snapshot was taken to show.
Moving or resizing a window. Tap a window's border to select it, then drag the border to move it or drag one of its four corner handles to resize it. Only the border, within a finger's width of the line, belongs to the window: a press anywhere inside it pans the drawing, so a large window never gets in the way of moving around the sheet. While a comment carrying a snapshot is open in the editor, its window is already selected and its handles showing. The first tap on a border only selects, so a drag that starts there on an unselected window still pans. On release the picture is rendered again from the PDF for the new window, at the same print resolution — not cropped from the old image, which would be a picture of the wrong place. The snapshot keeps its identity, so its comment, its quick-reference flag and its place in the report are unchanged, and the comment's bubble and stored position move to the new top-right corner. Markup drawn on the photo itself before snapshots stopped being drawable is carried across to the same place on the drawing. A window stays on its page and cannot be made smaller than the snapshot tool's own minimum. A move or resize is undoable with the sheet's Undo and Redo, and the comment's bubble goes back with it. To make that possible the replaced picture is kept on the device for as long as Undo can reach it; it is freed once Undo and Redo can no longer reach it — it falls off the end of the undo history, a new edit discards it from Redo, or the sheet is closed — and any still kept when the app is closed are freed the next time it starts.
Moving a comment's bubble is undoable the same way. An editor left open on that comment keeps the new position: it writes its note onto the comment as stored, not onto the copy it opened with.
Removing a snapshot keeps the comment. Remove snapshot is on the window's menu on the plan and in the snapshot viewer opened from the comment. The note, photographs and severity stay. When it was the comment's last snapshot, the comment is written at the position its bubble was drawn at, so the bubble does not jump back to where the comment was first placed, and it becomes an ordinary placed comment that can be moved again.
Quick references
A shelf of pictures the walk keeps within reach. It answers one thing the plan viewer could not do: read the schedule on E-401 while standing at the pedestal on E1.1. The bookmark button docked at the bottom left of the drawing opens a floating window over the sheet — drag its title bar to move it, drag the corner grip to resize it, and both are remembered on the device. The keyboard shortcut is R.
The dock is where the window lives when it is not open, not a toggle beside it: the window grows out of that button and the minimise control in its title bar folds it back into it, keeping its place, its size and whichever reference was showing. It rides with the bottom bar rather than the bottom of the screen, and it slides out from under the thumbnail rail when the rail is opened. The count on it says how many references the project has before the window is opened at all.
A window rather than a panel, because the whole value is that the drawing does not move: same sheet, same zoom, same place on it. It stays open while you step between sheets, and it can be left up over the comment editor, so a finding can be written with the schedule it is about still on screen.
Zoom is minus, a percentage, plus and fit, in a row under the picture — the same arrangement and the same meaning as the sheet viewer's own bar, where 100% is the picture at its own pixels. Zoom is anchored on the middle of what is on screen, so stepping in on a schedule keeps the row you were reading instead of jumping to the corner; a zoomed reference is panned by ordinary scrolling. Fit never enlarges a picture past its own resolution — a small reference blown up to fill the window is a blurred picture presented as the readable one, and the plus button is how you ask for that deliberately, with a percentage next to it. The zoom returns to fit whenever a different reference is brought up: a scroll position into one schedule means nothing in the next photo.
Anything already in the project can go on the shelf. Add to quick references appears in the photo markup editor, which is where you land when you tap a photograph, and in the full-size photo view, which is where a snapshot opens. In the markup editor it behaves like the ink: nothing is written until Done, so Cancel takes it back. From the photo view it is written straight away, because that view has no Done.
Or take one straight onto the shelf. The + at the end of the shelf menu takes or picks a photo — the platform offers the camera and the library, because half of what goes on a shelf is already on the phone — and several at once. Not everything worth keeping to hand is a finding: the panel schedule taped inside the door, a pump nameplate, a page of the O&M somebody handed over. Before this the only way to keep one was to invent a comment for it, which put it in the report.
A photo taken this way belongs to no comment, and it appears in neither export. That is not a filter applied on the way out — the record carries no comment and no sheet at all, so it is invisible to every per-comment and per-sheet query in the app, and both deliverables are built by walking the comments and asking what hangs off each one. It does not appear in the comment log, on any drawing, in the marked-up plan set or in the punch-list report. It is still a record of the project, so it syncs to the rest of the office and it counts against the device's storage.
Taking a reference off the shelf is the small × on its own thumbnail in the shelf menu, shown on the selected one only. It used to be a button in the title bar wearing a bookmark — the icon that means keep this — which was the one control in the window that threw a picture away; on the thumbnail the thing being removed is the thing under the finger. Because a photo taken straight onto the shelf has nowhere else to be, its × deletes it and asks first. Those references carry a small dot on their thumbnail so the two kinds are tellable apart before the × is reached. One that came off a comment is only unflagged, and needs no question.
The (i) in the title bar says all of this in four lines, inside the window, for the walk that meets it for the first time on a dock with no signal.
Assumptions and limitations. A quick reference is a flag
on the photograph — quickRef on the photo record — and not a
copy of it. So it syncs like any other change to that photo, it adds no
storage, and it lives and dies with the photograph:
deleting the photo, or the comment that carries it, takes the reference
with it. The shelf is per project, newest first, and it is
not named or ordered by hand; if a walk needs a dozen references, the
pictures themselves are what distinguish them. The shelf itself appears
nowhere in either export: it is a way of reading the plans, not a record of
the walk. A reference cannot be marked up — mark up the photograph or the
drawing it came from and the reference follows, and a photo taken straight
onto the shelf cannot be drawn on at all.
Text on a sheet — withdrawn
Writing loose words onto a drawing has been removed. Tapping a sheet now has one outcome: a comment, which carries a visit number, photos and a place in the log that becomes the report. Sheet text carried none of that and could not be tracked, so the same observation recorded two ways would have gone into a deliverable only one way.
Any sheet text already written is still drawn, in place, and is not editable or movable. It was a field observation, and those are not deleted because a feature was withdrawn. Re-record anything that matters as a comment.
The tool dock
Docked to the right edge, over the sheet: pen, eraser, ink colour, line width and undo. The tool decides what the stylus does — a finger still pans and pinches regardless.
Line width is a slider whose travel is spread across the offered nib widths rather than across the raw number, because those span more than tenfold and a linear scale would spend most of its length on widths nobody can tell apart. The preview dot is drawn at the size the nib will actually be on screen at the current zoom.
The eraser
It removes whole strokes, not parts of them. Splitting a stroke at the erase point would change what a markup is — one authored gesture — and make the question "who drew this" meaningless halfway along a line.
Its reach is a fixed number of screen pixels converted to sheet units at the current zoom, so it stays about a fingertip wide however far in you are, and a thick line can be erased without hitting its exact centre. With the eraser selected a single finger also erases, since a phone has no stylus; two fingers still pan and zoom.
Sharpness
Markup is drawn in a vector layer whose window onto the sheet moves as you zoom, rather than being scaled as an image. That distinction is why a line stays crisp at any magnification: scaling stretches pixels, while re-aiming the window redraws the geometry at the screen's native resolution. The plan itself is a raster from the PDF and is re-rendered on the same principle when you stop moving.
Text boxes
Drag sheet text to move it; tap it to edit. The grip at its end resizes it — dragging down enlarges. Position is stored as a fraction of the sheet and size as a fraction of sheet width, so both survive a different device and any zoom.
Not yet built
Arrows, measurement and its scale calibration, the punch list and the report are still to come, as is any sync between devices. The eraser removes whole strokes only — there is no partial erase.
Third-party components
PDF reading uses Mozilla pdf.js (Apache 2.0), vendored into the
app rather than loaded from a CDN so it works with no signal. Version
recorded in vendor/pdfjs/VERSION.
Calculation basis — Mechanical
Psychrometrics
ASHRAE Handbook — Fundamentals, Ch. 1, Hyland & Wexler formulation. Saturation pressure by eq. 5 (over ice, below 32 °F) and eq. 6 (over water, 32–392 °F). Humidity ratio eq. 20/21, wet bulb eq. 33/34, dew point eq. 37/38, enthalpy eq. 30, specific volume eq. 26. Barometric pressure from the standard atmosphere, eq. 3.
Wet bulb reported is the thermodynamic wet bulb, solved iteratively. It differs slightly from a sling psychrometer reading.
Coil and process loads
Computed from actual mass flow — m = cfm / v using the
entering specific volume — not the 4.5 and 1.08
standard-air shortcuts. At 80 °F/67 °F entering at sea
level the true factors are 4.33 and 1.06, so the textbook shortcuts
overstate capacity by about 3.7%. The gap widens with altitude and with
entering temperature. The app shows the effective factors so you can see
how far from standard air you actually are.
Duct
ASHRAE Handbook — Fundamentals, Ch. 21. Friction factor from the
Colebrook-White implicit equation solved by bisection (not an explicit
approximation). Absolute roughness values from Ch. 21 Table 1. Velocity
pressure Pv = ρ(V/1097)², which reduces to
(V/4005)² at 0.075 lb/ft³. Rectangular duct
friction uses the circular equivalent diameter
De = 1.30(ab)^0.625/(a+b)^0.25 with velocity taken from the
actual cross-section.
Air density is computed for dry air at the entered temperature and elevation — the ASHRAE standard-air basis, which returns 0.0749 lb/ft³ at 70 °F and sea level. Humidity moves density only a few tenths of a percent across the normal supply-air range, and assuming a humidity would be an unstated guess, so the app does not. The density actually used is always displayed.
Fitting loss coefficients are typical values only. They are adequate for sizing a run in the field. They are not adequate for a design of record — use the ASHRAE Duct Fitting Database entry for the specific geometry.
Pipe
Darcy-Weisbach with Colebrook friction factor, water properties (density, kinematic viscosity, vapor pressure) interpolated against temperature from standard steam-table values. Hazen-Williams is offered as a cross-check and is only valid for water near 60 °F in the turbulent range — it will mislead you on hot water, glycol, and low-flow branches. Inside diameters are catalog nominal values; confirm against the specified product. Valve and fitting K values are typical; confirm against published Cv for balancing, control, and strainer components.
Fans and pumps
Affinity laws with speed exponents 1/2/3. Impeller trim uses exponents 1/2/3; a geometrically similar fan wheel family uses 3/2/5. Both degrade outside roughly 80–115% of the reference point, and the app says so.
Plumbing — domestic water and sanitary drain
Basis is the 2021 International Plumbing Code, not the older editions the firm's spreadsheets cite. Water supply fixture units from Table E103.3(2); demand from Table E103.3(3); drainage fixture units from Table 709.1; building drain and sewer capacity from Table 710.1(1). Confirm against the edition the AHJ has adopted and its local amendments — plumbing is one of the most heavily amended trades.
Cold + hot exceeds the total on purpose. Table E103.3(2) note (a) sets each branch at three-fourths of the fixture total, because the cold peak and the hot peak do not happen at the same instant. Each column is summed independently and the total column is never computed as cold + hot. This differs from the firm's existing spreadsheet, which computes its total column as qty × (cold + hot) — so app totals will read lower than that sheet for any fixture with both supplies.
Demand takes the next tabulated row at or above the load rather than interpolating, which is the conservative reading and matches the spreadsheet's lookup behaviour; the interpolated figure is computed too so the two can be compared. Loads below the first row of a column use that first row and say so; loads past the last row return nothing rather than extrapolating. The flushometer-valve column is not tabulated below 5 wsfu. Hot water piping always reads the flush-tank column, per that column's own note, because it serves no flushometer valves. Continuous flows — irrigation, cooling make-up — are added in gpm after the conversion, never as fixture units.
Pipe size is read from fixture units, not from gpm. The service, cold and hot sizes come from the firm's Hunter's-curve table for Type L copper ("Updated Domestic Water Service Calcs", Total Domestic Water sheet): the load takes the first row at or above it, the same lookup the spreadsheet uses. The flush type selects the column — flush tank or flush valve. Marina reads the flush-tank column and raises any 1-1/4 in. result to 1-1/2 in. The hot side reads the flush-tank column (or the marina rule) in every case, per the E103.3(3) note above. That size table is the firm's, not an IPC table; its gpm column is E103.3(3) and its sizes keep Type L velocity near 8 ft/s, reaching 9.8 ft/s at 3 in. and 1,000 wsfu. The source of the size breaks is not cited in the sheet and needs the reviewing engineer's confirmation.
Demand in gpm and the velocity beside each size are information only. For another material the table's nominal size is kept, stepping up to the next size that material is made in where needed, and the velocity is computed from that material's inside diameter. There is no velocity limit; the size is always the table's. Continuous flow is in the velocity but not in the size. This is the field shortcut, not the IPC Appendix E segmented-loss method — it does not look at available pressure, developed length, meter and backflow losses, or elevation. For a service of record, run the pressure budget.
Sanitary sizes are reported at 1/2, 1/4 and 1/8 in./ft from Table 710.1(1). Any building drain or sewer serving a water closet is sized at 4 in. minimum, by firm standard; Table 710.1(1) note (a) requires only 3 in., and the app says when it has raised a size. Sizes below 3 in. are not tabulated at 1/8 in./ft at all. A 4 in. minimum for a building sewer with no water closet, which the firm's legacy SBCCI-based sheet carried, is not in current IPC Table 710.1(1); the app flags that question rather than enforcing it. Fixture-unit counts here size building drains, sewers, and horizontal branches of the building drain — not stacks, which use Table 710.1(2), and not vents.
Every fixture reads its loads from the code tables by row. The schedule offers each row of Table E103.3(2) once, paired with the Table 709.1 row that covers it: water closets and bathroom groups at the 1.6 gpf rows, showers at the 5.7 gpm-or-less row, urinals at the standard urinal row (709.1 note e). A fixture flushing more than 1.6 gpf or a shower above 5.7 gpm carries a higher drainage value in 709.1; check those by hand from the reference table. 709.1 fixtures with no E103.3(2) row follow — the nonwater urinal, dental fixtures, wash sink and floor drains — then the firm spreadsheets' fixtures that neither table lists. Floor sinks are left out: 709.1 gives them no value and sends them to Sections 709.4 and 709.4.1. Sections follow the order of the firm's domestic water demand sheet.
Fixtures marked * do not take their fixture units from the IPC tables. Hose bibbs, ice machines, bar sinks, clinical sinks, shampoo sinks, public dishwashers, institutional clothes washers and steam dryers carry the cold and hot values of the firm's spreadsheets; dental fixtures and wash sinks take the values of a comparable listed lavatory. Their total is computed by Table E103.3(2) note (a) — the larger branch is three-fourths of the total, rounded up to 0.1 — which reproduces every listed row of the table. Where 709.1 has no row, drainage is the Table 709.2 value for the trap: 4 in. for a clinical sink, 2 in. for a public dishwasher. Table E103.3(2) note (a) and Table 709.2 permit assigning an unlisted fixture by comparison with a listed one of similar flow and frequency, or by its drain/trap size. The commonly quoted 2.5 wsfu hose bibb is a UPC Table 610.3 value, not IPC. Those rows are engineering assumptions and the reviewing engineer confirms them; the app labels every one rather than hiding it.
Hunter's Curve under a project's Tools tab is this same
calculation. One engine, one set of tables, one set of
assumptions — everything above applies to it unchanged. What differs is
only what happens to the answer: a project tool keeps several named
tabs, saves them with the job, carries them between devices, and writes
one JSON file per tab into Tools/Hunter's Curve/ in the
project's folder on the server when you sync. Tabs are not
added together. Each is an independent schedule — a dock, a
building, a phase — and nothing in the app sums them, because a
combined service is a design decision about diversity and not an
arithmetic one. Those files are a record of what was entered and what
the app returned, on the same terms as every other number here: field
reference only, independently checked by the reviewing engineer before
it reaches a drawing, a calculation package or a submittal
response.
Storm drainage — roof drain sizing
The conversion constant is 0.0104 gpm per square foot per inch per hour, and it is derived rather than remembered. One in./hr over one ft² is (1/12 ft)(1 ft²)/hr = 0.083333 ft³/hr; at 7.480519 gal/ft³ that is 0.623377 gal/hr, or 0.0103896 gpm. The figure that arrived with this feature was 0.00104 — an order of magnitude low, which reports a 10,000 ft² roof at 4 in./hr as 42 gpm instead of 416 and sizes a 3 in. leader where 8 in. is needed. The validation suite re-derives the constant from the unit chain rather than restating it, so the smaller number cannot come back unnoticed.
Half the wall area is added to the roof area. That is the convention the firm’s sheet uses and what IPC 1106.4 states for a single adjacent wall: the vertical face catches wind-driven rain and half its area is the long-standing allowance. Selecting both faces of a parapet counts the wall twice before that halving. This is not IPC 1106.4’s full treatment — two or more adjacent walls at right angles have their own rules there, and the app halves whatever total it is given. The reviewing engineer confirms the wall allowance; the app states which one it applied.
Which roof a wall sheds onto is decided by geometry, not by how it was drawn. The app steps off the wall to its downhill side — the side the arrows point to — and credits whichever area is there; the smallest one wins, so a wall on the edge of a mechanical well feeds the well rather than the roof around it. A wall standing inside an area therefore feeds that area, one on a boundary feeds whichever side it faces, and flipping it moves the area across. Set to both sides it steps off each way, so a wall in the middle of one roof credits that roof twice — the doubling follows from the rule rather than being a separate case. A downhill side with no area drawn under it is still counted in the sheet total, because the rain still lands, but it belongs to no drain and the results card says how much.
The capacity table is the firm’s own sheet and its source is not confirmed. The horizontal figures are close to IPC Table 1106.2 but do not match it row for row, which says they were computed rather than transcribed. They are carried exactly as the sheet has them so the app agrees with the document the office already uses. What is verified is the table’s internal rule: flow goes as the square root of slope, so every row’s 1/4 in. column is its 1/8 in. column times √2 and the 1/2 in. column is twice it — the suite asserts that across all nine rows, which is an independent check on the transcription. The vertical leader figures at 12 in. and 15 in. are missing — the sheet carries a formula error there — and are stored as missing rather than as zero, because a zero would mean “carries no water” and silently oversize every leader past 10 in. A load past the last row returns no size rather than the largest one.
Grouping areas onto drains is an assignment, not a calculation. Which areas fall to which drain is an engineering judgement about slopes, crickets, and where the leaders can run — the app adds up whatever division it is given and sizes a pipe for it. It does not check that a division is drainable, that the areas in a group are contiguous, or that a drain exists where the label says. Primary and secondary are independent divisions of the same roof, because a roof has two independent systems: the primary drains, and the overflow drains that take over when a primary blocks. IPC 1106.3 sizes the secondary system for the same rainfall as the primary, which is why both use the same arithmetic over a different division rather than a different formula. An area assigned to neither still counts in the roof total; the app reports how many are outstanding rather than letting the totals look complete. The multi-select mode answers “what would these need between them?” and is deliberately kept nowhere — it is a question asked while deciding, not a decision.
Areas are measured off the plot scale you type, and that trusts the plot. A PDF page carries its paper size in its MediaBox — 72 points to the inch by definition — so the scale written on the sheet is exact. An imported image carries no paper size at all, so the sheet width has to be supplied. A sheet plotted “fit to page”, cropped, or reduced from 24×36 to 11×17 is no longer at its printed scale and nothing in the app can tell. Check a known dimension before relying on an area. Geometry is stored in page points rather than screen pixels, so a drawing survives rotation and a different device.
A locked tab is a note to yourself, not a review state.
The padlock stops a stray tap changing a finished schedule, travels with
the tab between devices, and appears in that tab's file as
"locked": true. It records only that whoever entered the
schedule considered it done — anyone who can open the tab can unlock it
in one tap, nothing about it is checked, and it carries no more weight
than the rest of this page.
Calculation basis — Electrical
Electrical — voltage drop
DC conductor resistance from NEC Chapter 9, Table 8 (uncoated
and coated copper, aluminum; solid conductors only through 8 AWG,
matching field practice under 310.106(A)). Ampacity from NEC Table
310.16 at the selected 60/75/90 °C column, 30 °C
ambient, ≤3 current-carrying conductors — no 310.15(B)/(C) correction
or adjustment factors are applied; apply those separately if they govern
the actual installation. Voltage drop is
Vdrop = R/ft × A × ft × multiplier,
multiplier 2 for single phase (current travels out on
one conductor and back on the other, so the drop sees twice the one-way
resistance) or √3 for three phase (the 120° phase relationship
between conductors reduces this from 2 to √3) — the standard
2 × K × I × D formula, matching the firm's
SuperiorVDCalc workbook. Provide any two of wire size, distance, and
amps and the third is solved for; providing all three checks the actual
drop for a known run. The drop is reported in volts with the
percentage of line voltage in parentheses, because the
specification is written in percent.
Solving for size: ampacity first, then voltage drop
When the wire size is solved for, the smallest conductor whose Table 310.16 ampacity carries the load is found first, and the voltage-drop search starts from there. Voltage drop is a performance limit; ampacity is a safety limit, and a conductor can satisfy the first while violating the second — which is exactly what happens on a short, heavily loaded run, where almost any size clears the drop allowance. The result names which of the two governed.
Table 310.16 — edition, corrections, and what is still unconfirmed
The ampacity table in this app is the 2026 NEC (NFPA 70‑2026) edition of Table 310.16. The 2026 edition revised 310.5(A) so the minimum conductor size became 16 AWG copper, 14 AWG copper‑clad aluminum, or 12 AWG aluminum, and added Table 310.16 values for those. CCA is not modelled in this app, and only the long-standing 90 °C value is carried for 16 AWG copper — published sources disagree on its 60/75 °C figures, so those are left blank rather than guessed.
Four cells were corrected on 25 August 2026, three of which overstated ampacity:
| Cell | Was | Now | Direction |
|---|---|---|---|
| 14 AWG Cu, 60 °C | 20 | 15 | overstated by 33 % |
| 12 AWG Cu, 60 °C | 25 | 20 | overstated by 25 % |
| 12 AWG Al, 60 °C | 20 | 15 | overstated by 33 % |
| 3 AWG Cu, 90 °C | 110 | 115 | understated |
The first three shared one cause — the 75 °C figure duplicated into the 60 °C column — and were reachable from the voltage-drop screen's 60 °C option, so a 20 A load on #14 copper was returning a pass. Any voltage-drop or ampacity result produced by this app before v0.6.39 that used the 60 °C column should be rechecked. The 75 °C column, which is the default and the one the reference legends are built on, was correct throughout.
The suite now checks the entire 60 °C copper column cell by cell, plus three structural rules that hold across every row: ampacity rises strictly with temperature rating, aluminum is always below copper at the same size, and ampacity never falls as size rises. All three of the overstated cells violated the first rule, which is how a transcription error of this kind is caught without anyone having to think to spot-check that particular cell.
Eight cells remain unconfirmed. A secondary source disagrees with the value carried here and no printed copy was available to settle it. These are not silently trusted: selecting one produces a warning on the result, and it must be confirmed against a printed 2026 NEC before it reaches a stamped drawing.
| Cell | This app | Source disagrees |
|---|---|---|
| 1 AWG Cu, 90 °C | 150 | 145 |
| 6 AWG Al, 90 °C | 60 | 55 |
| 8 AWG Al, 60 °C | 30 | 35 |
| 300 kcmil Al, 60 °C | 190 | 195 |
| 300 kcmil Al, 90 °C | 255 | 260 |
| 600 kcmil Cu, 60 °C | 355 | 350 |
| 1500 kcmil Cu, 60 °C | 520 | 525 |
| 2000 kcmil Cu, 60 °C | 560 | 555 |
The result card also now states, on every calculation, that the ampacity shown is the raw table value with no 310.15(B) ambient correction and no 310.15(C) conduit-fill adjustment applied. That qualification used to live only on this page, while the calculator itself reported an unqualified pass.
Electrical — marina shore power load
Demand factor by total receptacle count per NEC Table 220.120 (2023 NEC). This citation was previously shown in this app as 555.12(B), which is the 2017 designation; the table became Table 555.6 in the 2020 NEC and Table 220.120 in the 2023 NEC. The bracket values are identical across all three editions, so no calculated result changed — only the reference. Cite the designation belonging to the edition the AHJ has adopted. Connected load: a 50A/125-250V receptacle counts at its full 50A nameplate rating. A 30A/125V single-pole receptacle counts at 15A, half its nameplate rating — a firm convention, confirmed for this app: the 50A receptacles are two-pole 240V loads that draw on both ungrounded conductors of the shared feeder, while a 30A/125V receptacle is single-pole and loads only one of the two conductors, so on a balanced multiwire feeder carrying both load types its contribution to either conductor is taken as half of nameplate. This assumption is specific to a shared 120/240V multiwire feeder mixing both receptacle types — confirm it still applies to the feeder configuration on the project before using the result. A metered installation (individual kWh submeter per slip, NEC 220.120 criteria) gets the 0.9 multiplier per the firm cheat sheet's note 2. General-use 20A/125V duplex receptacles are counted at 180 VA each (Table 220.14(I)) with the Table 220.44 demand factor (first 10 kVA at 100%, remainder at 50%). Pedestal lights are counted at 9 VA each ×1.25 continuous-load factor (210.19(A)/215.2). All electrical amp figures assume a 240V basis for the duplex/pedestal-light adders, matching the source workbook.
The "Capacity around this result" grid on the Marina tab is a sensitivity sweep, not a second calculation — every cell calls the same demand-load function above at a nearby qty50/qty30, the way the source workbook's "Demanded Load" sheet sweeps receptacle counts against Max Amps and conditionally formats the result. It is a window, not the full sheet: the "Range to check" field sets how many receptacles up or down from the current selection are shown on each axis (default ±2), and green/red mean the same over/under-capacity comparison against Max Amps as the pass/fail note above it, not an independent check.
Not included by design: AIC/available fault current rating and standard breaker-size selection are not implemented as calculators here — use the reference legends for conductor/conduit selection and confirm fault current and breaker selection against the project's actual utility/transformer data and the panel schedule.
Reference legends (service entrance, portable cable, and branch circuit/feeder tables) are transcribed from the firm's cheat sheet and cross-checked row by row against this app's own Table 310.16 data — see the validation suite, section 25. They are quick-reference only; the project's issued conductor/conduit schedule governs.
Electrical — grounding reference tables
NEC Table 250.122 (equipment grounding conductors) and NEC Table 250.66 (grounding electrode conductors) are transcribed reference tables, 2023 NEC, not calculators — the app does no grounding sizing of its own. Each was transcribed against two independent published sources and then cross-checked against this app's own NEC Chapter 9 Table 8 resistance data: for every row, the aluminum entry is verified to be within 25% of the copper entry's resistance per 1000 ft, which is the physical relationship both tables are built on and an independent check on the transcription rather than a restatement of it. See the validation suite, section 32.
Table 250.122 is truncated at 3000 A on purpose. Published transcriptions of the aluminum column disagree at the 4000/5000/6000 A rows (750 vs 800 kcmil, 1200 vs 1250 kcmil), and the disagreement was not resolvable without the printed code, so those rows were omitted rather than guessed. Nothing the firm designs approaches that range.
Neither table's adjustment rules are automated. In particular 250.122(B) — proportional upsizing of the EGC when the ungrounded conductors are increased for voltage drop — applies routinely to long pier feeders and must be applied by hand. The 250.66(A)/(B)/(C) caps for rod, concrete-encased and ground-ring electrodes likewise are stated as notes only.
Validation
The calculation engine ships with a test suite of 272 assertions checked against published reference values — ASHRAE psychrometric tables, the ASHRAE duct friction chart and equivalent-diameter tables, the Moody diagram, AHRI rating conditions, steam tables, copper tube and Schedule 40 friction tables, NEC Chapter 9 Table 8, Table 310.16, Table 250.122 and Table 250.66, IPC Tables E103.3(2), E103.3(3), 709.1 and 710.1(1), and NIST unit factors. Pipe results are cross-validated between Darcy-Weisbach and Hazen-Williams, which share no code; electrical reference legends are cross-validated against the app's own ampacity table; and the IPC fixture-unit transcription is cross-checked against Table E103.3(2)'s own three-fourths rule, which is an independent constraint on the numbers rather than a restatement of them.
Run it with node validation.test.mjs. Every assertion
names its source. Treat a failing suite as a stop-work condition on this
app, and re-run it after any change to engine.js.
Known limitations, stated plainly: no glycol or steam properties; no duct or pipe insulation heat gain; no acoustics; SMACNA pressure-class and gauge selection is not implemented; the psychrometric chart is drawn for the entered elevation only; electrical calculators assume the 2023 NEC article/table numbers cited above and do not check 310.15(B)/(C) correction/adjustment factors, conduit fill, or AIC/breaker sizing; and the plumbing tool sizes building drains and sewers only — no stacks (IPC Table 710.1(2)), no vents, no traps or trap arms, no grease or sand interceptors, no storm drainage, and no Appendix E pressure budget for the water service.