Validation
Trust in a simulator comes from checkable outputs, not from who wrote it. Loft is validated three ways: against first-principles physics, against OpenRocket's own stored results, and against an independent flight simulator (RocketPy).
Against physics (the test suite)
The engine's core is checked in the test suite (lib/**/*.test.ts, run in CI on every change):
- A drag-free vacuum flight reproduces the closed-form burnout velocity and apogee to within a fraction of a percent — a direct check of the thrust, gravity, mass, and RK4 integration.
- Descent under a parachute converges to the analytic terminal velocity
√(2mg / ρ·C_d·A). - A cone nose gives Barrowman's exact
C_Nα = 2and centre of pressure at two-thirds of its length; a hollow tube's mass matches its geometry exactly. A conical transition reproduces its hand-computed Barrowman normal force and CP — negative (destabilizing) for a boattail — and a point-to-radius transition recovers the cone-nose result exactly, cross-checking the two body terms against each other. - The standard atmosphere matches published sea-level and 11 km values; higher impulse monotonically raises apogee.
Against the file's own tool
A design file you already simulated carries that tool's own stored flight results — an .ork carries OpenRocket's, a .rkt RockSim's, a .CDX1RASAero's. When you import such a file, Loft flies it under the same stored launch conditions and diffs each metric — apogee, velocity, Mach, timings — reporting the signed error and the mean absolute percentage error (MAPE). That comparison appears right in the results, labelled with the tool that actually produced the numbers, and the method is in lib/validation/compare.ts.
A file's stored runs are not all the tool's current answer, and the file says which is which. OpenRocket stamps each simulation with a status: outdated means the run predates the design's last edit, so it describes an earlier version of the rocket, and notsimulated means the figures are carried in the file for a simulation the tool does not consider run. Both are shown — they are still a reference point — but labelled, since calling either “OpenRocket vs Loft” would credit a current prediction to a tool that did not make one. This is not a rare edge: re-measured across the corpus in August 2026,8 of 79 stored OpenRocket runs are outdated and 7 more are marked not simulated. The accuracy census below includes them, which is the honest picture rather than the flattering one; measured separately they agree with Loft less closely than the up-to-date runs, not equally — median apogee disagreement 3.7% against 2.0%. The previous text here said 3.3% against 2.1% and called them comparable, which understated the gap on exactly the runs this page flags.
The comparison is shown only when Loft flew the complete design. If the design includes something Loft simplifies — pods, parallel boosters, a ring tail — the stored results describe a different flight than the one simulated, so the comparison is withheld rather than reported as a misleading error. (Serial staging, motor clusters, and tube fins are simulated, so those still get a comparison.) A single-stack design gives the honest, like-for-like check.
Many .ork files store more than summary numbers — they carry OpenRocket's per-step flight log, the whole trajectory and drag coefficient it computed step by step. When that log is present, Loft overlays its own solver on it directly: altitude versus time, and — the interesting one — drag coefficient versus time through the ascent, Loft's curve against OpenRocket's. That drag curve is a genuinely independent per-step oracle from a different engine, not just an endpoint diff, so where the two curves sit apart you can see exactly when and where the drag models diverge, rather than inferring it from an apogee gap. A deployed parachute's coefficient (referenced to the body it runs into the tens) is left off the drag curve, which is about the airframe's own drag on the way up. The overlay is quantified too: Loft's ascent drag curve is interpolated onto each stored sample and the mean gap reported as a single figure (both a percentage and an absolute Cd), so “the two engines' drag agree to about X%” is a number you can read, not an eyeball. The overlay appears in the results whenever an imported file carries the log and Loft flew the design as stored; the series and the agreement figure are built in lib/validation/crosscheck.ts.
The honest oracle is your own design. The bundled samples below ship with author-estimated stored figures (Loft can't run OpenRocket here), so their comparison is a demonstration of the mechanism, not an accuracy claim. Import a real file for a real check.
What the bundled samples do and don't tell you
The designs Loft ships as one-tap examples are its own. No OpenRocket run ever produced flight numbers for them, so they carry none: their files state the simulation and its launch conditions and nothing else, and the stored-results panel simply does not appear for a sample. It used to appear, against figures that had been written by hand to give the panel something to show — figures that did not even hold together, one claiming a 2,250 m apogee at the same 20.2 s time-to-apogee at which Loft reaches 2,940 m, which no ballistic coast does. A demonstration is not worth a number that isn't true. Import your own simulated .ork or .rkt and the comparison runs against the numbers your tool actually stored.
The two validations on this page that are real are below and above: the RocketPy cross-check, which runs an independent engine over the same designs, and the corpus of real in-the-wild design files, each carrying its own tool's stored results — measured in the repository's corpus suite rather than here, because those files are other people's designs and aren't redistributed.
What the corpus says, metric by metric
Across the corpus — 35 design files from OpenRocket, RockSim and RASAero, carrying 97 stored simulations that Loft flies completely — this is the median absolute disagreement with each file's own stored result. It includes the cases the suite excuses as known issues, so it is the honest picture rather than the flattering one.
Each figure names the population it is measured over, and they are not the same. Only apogee reaches all 97: a metric is compared where a file stores it, the formats do not store the same set, and a figure a file repeats unchanged across its runs is counted once rather than once per run. Max Mach is OpenRocket-only — neither RockSim nor RASAero writes it — so reading it as a corpus-wide figure would credit Loft with an agreement two of the three tools were never asked about. This page used to print one “97” above the whole list.
- time to apogee 1.5% (94), rail-exit velocity 1.9% (73)
- max Mach 1.7% (68, OpenRocket only), max velocity 1.9% (96), optimum delay 2.4% (80)
- apogee 2.9% (97), max acceleration 1.3% (80), flight time 2.8% (82)
- ground-hit velocity 0.8% (80), deployment velocity 5.1% (81)
A comparison is counted once, however many times a file repeats it. A design's stored runs vary whatever their author was interested in, and a quantity none of those inputs reaches comes out the same every time — from the file, and from Loft. One RockSim design in the corpus stores fifteen runs that differ in rail length and ejection delay, and whose apogee accordingly ranges from 323 m to 2,101 m. Its stored max acceleration is 125.291 m/s² on all fifteen, and Loft returns 136.345 on all fifteen: peak acceleration comes from the thrust spike, which is over before the rocket clears even the short rail and long before any ejection charge, so neither input can reach it and both tools agree it doesn't move. That is one disagreement of +8.8%, not fifteen — and it was carrying fifteen times the weight of any other design's. Counting each comparison once — and only where the stored value and Loft's value both repeat, so a disagreement that genuinely varies per run still counts every time — moved max acceleration from 3.2% to 1.8% and rail-exit velocity from 1.9% to 1.6% as they stood that day; both have since moved again with the ring-bore correction below, to 1.3% and 1.9%. It is the second reason a population above can be smaller than 97, alongside a format simply not storing the metric: 54 of the 910 comparisons were repeats.
Two things that rule does not do, said here rather than left for a reader to find. It de-duplicates identical values, not identical runs— nine of that design's fifteen runs are identical in every stated input and differ only in RockSim's own turbulence draw, so on apogee they still count nine times. And a median over one row per design is less sensitive to a single design going wrong than a median that counted it fifteen times, so the corpus suite separately counts how many designs' max acceleration sits far out, and fails when that number grows.
Ground-hit velocity and flight time, measured under canopy
Two of those are over flights that came down under a canopy. Ground-hit velocity and flight time are reported over the 82 stored runs that are not ballistic — 70 where the file states a recovery device came out, and 12 where it states nothing either way. Both formats record it: OpenRocket writes a recoverydevicedeployment event into the flight log it saves with each run, and RockSim records it per device. The other 12 came down with nothing out — one design in the corpus stores eleven plugged runs against four canopy ones — and they are worse, on their own line rather than averaged in: ground-hit velocity 14.9%, flight time 4.8%. A descent at 160 m/s and a descent at 9 m/s are different flights, and a median over both is a number about neither. The ballistic figure is the weakest number on this page, which is the point of separating it: Loft's no-recovery descent is where its drag model is furthest from a tool that has one, and pooling it with canopy descents hid that in both directions. Ground-hit velocity lists 80 of those 82 rather than all of them, two being repeats of a run already counted.
Ground-hit velocity went 3.0% → 8.3% → 2.0% → 1.3% → 0.8%, and not one of those moves was the engine. All three were the same mistake found in three places: Loft's figure and the stored figure it was scored against were not always the same physical quantity, and nothing recorded which was which. Loft reports the descent rate— the vertical speed — because wind moves the speed over the ground without making the canopy any smaller, and the rules of thumb and the per-section landing energy a waiver is judged on are all descent rates.
- 3.0% was two errors cancelling. Loft measured the total speed over the ground under a name that means the descent rate, and its own descent ran low; one design reads 14.5% low on descent rate but only 3.0% low once the wind term is added back. Reporting the honest figure took the census to 8.3%.
- RockSim stores the total. Its
VelocityAtLandingis the magnitude of its own three components — verified on every stored simulation in the corpus — so reading the vertical component instead moved the RockSim median from 25.7% to 21.9%. - And OpenRocket changed its own convention.Files written by 23.09 or earlier store the air-relative speed, which under an open canopy is the descent rate; files written by 24.12 or later store the ground-frame total, drift included. That is read from OpenRocket's own source rather than inferred, and 64 of the corpus's 91 OpenRocket simulations are on the newer side. Comparing each file against the quantity its own version actually stored took the OpenRocket median from 7.8% to 1.2%.
- And eleven of the comparisons were lawn darts.One RockSim design stores fifteen runs of the same rocket: four with its parachutes out, and eleven plugged, coming down ballistically at 83–162 m/s. The file marks which is which, per recovery device, and Loft was reading neither mark — so eleven no-recovery descents were being averaged into a figure describing canopy ones. Counting them separately took the canopy figure from 2.0% to 1.3% and put the ballistic runs on the line above at 14.9%.
So that figure — 1.3% then, 0.8% after the ring-bore correction below — is the first here that measures what it always claimed to: how far Loft's descent under a canopy is from the tool's, rather than how far a vertical speed is from a total one, or a lawn dart from a parachute. The remaining gap is Loft's own, and it is now the smallest number in the census rather than three times its worst.
Deployment velocity, and why it reads as an outlier
Deployment velocity looks like the outlier and mostly isn't: it is an ill-conditioned metric, not a badly modelled one. Near apogee the rocket is barely moving, so the opening speed is roughly g times however far past apogee the charge fires — and a tenth of a second of timing difference between two simulators is about 1 m/s on a number whose whole value is a few m/s. Split by how slow the opening is, the absolute error barely moves while the percentage swings wildly: openings under 5 m/s disagree by 23% but only 0.5 m/s; openings over 15 m/s disagree by 3.3% and 0.9 m/s. Read it in m/s, not percent. The genuinely wrong deployment cases are elsewhere and are listed as known issues in the suite.
One of them was not a deployment error at all — it was a flight with nothing out, scored as though it had opened at 0 m/s. Loft reports 0 for the deployment speed of a flight where no device opened, the same way it reports 0 for the landing speed of a flight that never reaches the ground: a sentinel meaning this did not happen, not a measurement. Against a file that stores a real figure, that came out of the arithmetic as a flawless −100% — a confident disagreement about a number neither tool measured. One corpus design does exactly this, and its own stored figure is 33.4 m/s. Because that row also entered the mean, the panel reported this design's mean absolute error as 48.7% when the metrics it could actually compare disagree by 42.3%: the fault made Loft look worse than it is, on the one page whose job is saying whether Loft can be trusted at all. A metric whose event never happened is now withheld and named under the table rather than scored, so a row is only ever a disagreement between two answers.
Optimum delay: one word, two meanings
Optimum delay is where two formats mean different things by the same word. OpenRocket stores the free-coast delay — the time from burnout to the apogee the rocket would have reached with nothing out — which is the number a flyer buys a motor against, and the one Loft reports. RockSim stores the delay of the run it actually flew: TimeToApogee minus TimeToBurnout, exact on every stored simulation in every RockSim file in the corpus. On a design whose canopy opens at burnout those are not the same flight. One corpus design stores four such runs at 1.34 s, against Loft's free coast of about 16 s — four census rows reading +1107% for a delay model that was not wrong. Comparing each file against its own convention takes those rows to −21% and the worst optimum-delay disagreement anywhere in the corpus from 1107% to 59%.
The published median did not move, and that is worth saying rather than hiding. Four of optimum delay's rows changed by a factor of eighteen and the median stayed where it was — 2.5% over 84 rows as it was measured then, 2.4% over 80 now — which is what a median is for, and why it was the wrong instrument to catch this. The suite now asserts the worst optimum-delay row as well as the median, because a row comparing two different flights is a different defect from a metric that is simply off.
What moved on 9 August 2026, and why
Nine of these figures moved on 9 August 2026, and the cause was a file-reading bug rather than anything in the solver. OpenRocket writes auto for a centring ring's inner diameter, meaning “the hole the motor mount needs” — the mount already states it. Loft read that as a missing number and fell back to the outer diameter less a wall thickness the file had not given either, which resolves to no hole at all; and a ring with no hole, measured as π(r² − r²)L, is a ring with no metal. Those rings weighed nothing. One corpus design carries four aluminium rings of 152.3 mm outer diameter that imported at 0 g against about 210 g each — 840 g of a 12,620 g airframe, 6.7%, at four fixed stations, so its centre of gravity and stability margin were computed without them too. Reading auto as the mount it holds moved seven medians closer to the tools Loft is checked against: deployment velocity 6.2% to 5.1%, ground-hit velocity 1.3% to 0.8%, max acceleration 1.8% to 1.3%, max velocity 2.2% to 1.9%, max Mach 2.0% to 1.7%, apogee 3.1% to 2.9% and flight time 3.1% to 2.8%. One moved the other way and is printed here for the same reason: rail-exit velocity 1.6% to 1.9%. A heavier vehicle leaves the rail more slowly, which is the direction a mass correction pushes; publishing the seven and not the one would make the list an advertisement rather than a measurement.
And the same principle applied per design says two of them got worse, not better. A median hides what happens to individual files, so both were measured. OpenRocket's own Base drag hack (short-wide) went from −3.0% to −8.8% on apogee, and its Airstart timing example from +0.2% to −2.7%, with all five of its stored simulations moving the same way. Both carry centring rings that leave both radii automatic, and on both the ring stopped being a 1.5 mm hoop and became the annulus the file describes — 5.4 g to 155.8 g each on Airstart timing, which moves that design's dry mass 9,735 g to 10,036 g and its centre of gravity 855 mm to 902 mm from the nose. The geometry Loft now computes is the geometry OpenRocket's own source computes from the same file, so the earlier closer agreement was resting on a compensating error somewhere else in the model rather than on being right. Where that error is has not been established, and until it is this is a worse number honestly arrived at. Both designs remain inside the ±12% the corpus asserts.
Deployment velocity went 6.0% to 6.2% before that, and that was the honest direction. It was an OpenRocket-only figure standing in a cross-tool census: RockSim stores a deployment velocity too, in a tag it misspells as VelocityAtDeplyment, and Loft read none of it. Six more comparisons joined, and they disagree by more than the OpenRocket ones, so the published median rose while the measurement got better. Separately, OpenRocket's stored figure is the speed at the last device to open, where Loft reports the fastest— the opening shock a flyer sizes a shock cord against, which is deliberately not the smaller of the two. Comparing each against the event the file describes took the OpenRocket median from 6.0% to 5.6% at the time.
One stored run is a charge firing with nothing out — RockSim records about 234 m/s for it, where the same design's canopy runs store 10 to 33 — and Loft reports no deployment at all, correctly. It is counted on its own line rather than averaged in, the same treatment the ballistic descents get, because a “deployment velocity” for a flight with nothing deployed is not the same quantity.
How these figures are asserted
These figures are asserted, not just written down: the corpus suite recomputes the census on every run and fails if any metric drifts from what this page claims, so a change to the engine either keeps the numbers true or forces them to be updated. It fails in both directions. It used to fail only when this page was too generous to Loft, on the reasoning that getting better is always allowed — but a page claiming 3.2% when the measurement is 1.3% is just as wrong about Loft, and it is the direction nothing goes red on. Getting better is still always allowed; it has to be published in the change that earns it. The run prints the current figures so that is one edit.
Against RocketPy (an independent engine)
Loft is also cross-checked against RocketPy, a mature, open-source 6-DOF flight simulator independently validated against real recorded flights to within a few percent. It shares none of Loft's code. For each bundled design, RocketPy flies the same rocket and the two engines are compared metric by metric. RocketPy takes a drag coefficient rather than deriving it from the shape, so it is fed Loft's own drag curve.
Because the drag is held equal, this is an independent check of the trajectory integrator, the mass model, the off-the-rail velocity (the safety-relevant departure speed, resolved at the exact rod-length crossing), and — from RocketPy's own Barrowman solver — the centre of pressure and static margin. It is not an independent drag check; that is what OpenRocket's stored per-step drag (above) provides. The two oracles are complementary: RocketPy pins the flight mechanics, OpenRocket pins the drag.
The descent is cross-checked the same way. For a design that carries recovery, both engines fly on to the ground under one equivalent canopy carrying the design's landing drag area (Cd·A— every deployed device plus the body's own descent drag), with wind zeroed so the impact speed is the vertical terminal. Holding that drag area equal makes the landing speed and landing energy — the recovery-adequacy figures a flyer actually cares about — a clean check of the descent integrator and the burnout mass against an independent engine. The two agree to within about a tenth of a percent on every bundled design. (RocketPy also applies the airframe's drag curve on the way down, a sub-percent addition at a few m/s, so exact agreement isn't expected; the staged drogue-then-main sequence — which changes descent time but not the terminal speed — isn't replayed, so time-to-land is not compared.)
The designs below span the geometry the centre-of-pressure model has to get right: a constant-radius airframe on trapezoidal fins, a transonic flight, and a design with a boattail and elliptical fins — the two Barrowman terms most easily gotten wrong. The two engines agree on the static margin of every one to within a few hundredths of a caliber.
The ascent comparison is ballistic — recovery and wind removed on both sides — so the coast runs to the true apogee with nothing to confound the physics. The figures below are RocketPy's committed output (v1.12.1), while the Loft column is computed live in this build — so the gap you see is always current with the engine. And unlike the author-estimated “stored” figures above, these RocketPy numbers are a genuine independent simulation.
And you can run RocketPy on your own design, in your own browser. The Cross-check workspace boots RocketPy under Pyodide and flies whatever you have imported, so the comparison below is a fixed reference set and the one on your design is live. Nothing leaves the browser. It is a large download the first time (about 40 MB of Python runtime) and takes a few seconds to start, which is why it loads only when asked for. This paragraph replaces one that said RocketPy “doesn't run in your browser” — true when the reference table was the only cross-check here, and untrue since the second solver shipped.
Loft Demo 38mm — motor comparison (G40W) — largest difference 0.2%
| Metric | RocketPy | Loft | Δ |
|---|---|---|---|
| Apogee | 548 m | 547 m | -0.2% |
| Max velocity | 109 m/s | 109 m/s | 0% |
| Max Mach | 0.32 | 0.32 | 0% |
| Time to apogee | 10.6 s | 10.6 s | -0.1% |
| Rail-exit velocity | 14.1 m/s | 14.1 m/s | +0.1% |
| Static margin | 4.51 cal | 4.51 cal | 0% |
| Landing speed | 6.8 m/s | 6.8 m/s | +0.2% |
| Landing energy | 15 J | 15 J | +0.1% |
Loft Demo 38mm — boattail + elliptical fins (H128W) — largest difference 0.6%
| Metric | RocketPy | Loft | Δ |
|---|---|---|---|
| Apogee | 907 m | 905 m | -0.1% |
| Max velocity | 187 m/s | 187 m/s | 0% |
| Max Mach | 0.55 | 0.55 | 0% |
| Time to apogee | 12.4 s | 12.4 s | -0.2% |
| Rail-exit velocity | 21.3 m/s | 21.3 m/s | -0.1% |
| Static margin | 3.84 cal | 3.82 cal | -0.6% |
| Landing speed | 7.3 m/s | 7.3 m/s | +0.1% |
| Landing energy | 21 J | 21 J | +0.5% |
Loft Demo 38mm — single deploy (H128W) — largest difference 0.2%
| Metric | RocketPy | Loft | Δ |
|---|---|---|---|
| Apogee | 994 m | 993 m | -0.1% |
| Max velocity | 205 m/s | 205 m/s | 0% |
| Max Mach | 0.6 | 0.6 | 0% |
| Time to apogee | 12.8 s | 12.8 s | -0.2% |
| Rail-exit velocity | 22.2 m/s | 22.2 m/s | +0.1% |
| Static margin | 4.07 cal | 4.07 cal | -0.1% |
| Landing speed | 6.9 m/s | 7 m/s | +0.2% |
| Landing energy | 17 J | 17 J | +0.1% |
Loft Demo 54mm — dual deploy (K550W) — largest difference 0.6%
| Metric | RocketPy | Loft | Δ |
|---|---|---|---|
| Apogee | 2,957 m | 2,941 m | -0.6% |
| Max velocity | 437 m/s | 436 m/s | 0% |
| Max Mach | 1.29 | 1.29 | -0.1% |
| Time to apogee | 20.3 s | 20.2 s | -0.6% |
| Rail-exit velocity | 28.2 m/s | 28.2 m/s | -0.1% |
| Static margin | 3.06 cal | 3.06 cal | +0.1% |
| Landing speed | 5.3 m/s | 5.3 m/s | +0.1% |
| Landing energy | 28 J | 29 J | +0.4% |
Cross-checks against real OpenRocket files
Beyond the bundled samples, Loft has been run against genuine OpenRocket files (which carry real stored results, including OpenRocket's own per-step drag coefficient). One you can reproduce yourself: open OpenRocket's own bundled example “A simple model rocket”(v24.12) and import it here. On its coast, Loft now reproduces OpenRocket's total drag coefficient closely across the whole flight — boosting and coasting, friction/pressure/base each within a few percent (Cd ≈ 0.855 near burnout) — after Loft was taught to read the fins' square edge cross-section and count its leading-edge stagnation and trailing-edge base pressure drag. It also tracks the coast-drag rise as the rocket slows toward apogee — where OpenRocket's Cd climbs to ~0.97 at a few m/s, Loft was earlier stuck near 0.85 (a ~13% under-count) but now lands within ~2%, having been corrected to a fully-turbulent boundary layer whose friction climbs at low Reynolds number rather than an inappropriate laminar branch. And the base drag now carries in full through boost (as OpenRocket's does), which had been discounted — a fix that most matters for a body much wider than its motor, where the exhaust fills little of the base. All five of the file's stored simulations now land within a few percent: the three C6 flights within ~0.4%, the B4 at +1.9%, and the low-impulse A8 at +3.6%(under 2 m on a 50 m flight, where a slow, near-drag-free A8 leaves little to model). Driving this file is itself what caught a mis-sourced B4 thrust curve: the bundled B4 had been an over-energetic data file (5.02 N·s — just over the 5.0 N·s ceiling that definesa B motor, averaging ~5 N rather than 4), which flew the B4 ~26% high. It now uses the NAR-certified curve (4.30 N·s, avg 4.2 N, published on the ThrustCurve.org B4 page), and a unit test now pins every bundled Estes curve to its certified impulse so a wrong data file can't slip back in. The shared drag model fits all three motors. To reproduce: import the file, pick each simulation's configuration, and read the OpenRocket-vs-Loft panel. (The file isn't bundled — it ships with OpenRocket, which is GPL.)
Driving more of OpenRocket's own example files the same way turned up two drag fixes. Its rounded-fin examples (the “deployable payload” and “3D-printable” designs) coasted ~18–20% draggier in Loft than in their stored curves, because a rounded fin leading edge was modelled as half a square one; a radiused edge in fact attaches the flow with no stagnation face, so its leading edge now carries only the compressibility term (like an airfoil), bringing those coasts to within ~4–9% of OpenRocket's Cd. And a design that models its fins as several separate one-fin sets (the “ARC payload” example: three sets, one fin each) had its fin frontal area — hence pressure drag — counted from a single set, reading ~14% low on total Cd; summing over sets brings it within ~4%. Both are reproducible by importing the file and reading the per-step panel; neither file is bundled (GPL).
Taken together, that example set is a broad accuracy check. Across OpenRocket's nine bundled example designs — 33 flight configurations spanning small A-class models through J-class high-power, single-stage through three-stage — Loft's apogee lands within 10% of OpenRocket's stored value on every configuration, and within 5% on most (median ~2%). Max velocity agrees just as closely (median ~2%, every flight within 10%), and rail-exit velocity typically to about 1%. Peak acceleration is within ~5% on most; the exceptions are a few very high-thrust motors whose brief thrust spike Loft's fixed-step integrator still slightly under-resolves (reading a little low) and one three-stage sustainer (a little high) — each a known, bounded residual rather than a drag or mass error, since apogee and velocity on those same flights agree. These are point-in-time figures you can reproduce by importing the files and reading the OpenRocket-vs-Loft panel; none is bundled (they ship with OpenRocket, which is GPL).
Motor curves vs certification
Every bundled thrust curve is authentic ThrustCurve.org data. As a standing check, each curve's integrated total impulse is compared against the motor's ThrustCurve certified value, and the published curve closest to certification is the one bundled: all of the sixty-plus curves land within about 8% (most within 2%). The one exception is the AeroTech F50T, whose only published RASP curve integrates ~11% below its certified total impulse — it under-states (the conservative direction for altitude), and no closer curve is published to bundle in its place. Thrust-vs-time is factual test-stand data, so this is a data-provenance check, not a tuning knob.
Staged flights
Serial staging is checked the same way — against OpenRocket's own stored results for its bundled multi-stage examples. On the “Two stage high power rocket”(a booster and sustainer, each on an AeroTech H148R, with a drogue at apogee and a main at 152 m), Loft reaches 663 m against OpenRocket's 675 m (−2%), with the burnout velocity matching to about 1% and the separation, drogue, and main all firing in the right order. That example's second configuration — a long-burn AeroTech I59WN booster staging to a fast I357T sustainer — now flies too (both curves are authentic ThrustCurve.org data), landing within ~1% of OpenRocket's stored figure. On the “Three stage low power rocket” Loft lands within ~4% of OpenRocket's apogee across all three configurations, reading a little low. Stability tracks too, now that the stages stack into one airframe: the loaded centre of gravity matches OpenRocket's stored value — 1.33 m vs 1.33 m on the two-stage, within ~3% on the three-stage — and the sustainer's own post-separation margin is reported and flagged if it falls below 1 cal. To reproduce, import either OpenRocket example (they ship with OpenRocket, which is GPL, so they aren't bundled here).
Community validation cases
The most valuable record is predicted-vs-actual: Loft's prediction against a real recorded flight (an altimeter apogee, a tracked descent). If you've flown a design you're willing to share, open an issue with the .ork, the motor, and the measured result — verified cases will be collected here as an ongoing accuracy record. Until then, this page is honest about being early.
See also the limitations log for where to expect the largest differences.