About this calculator
This calculator helps design a U-shaped (180° turn) stair with a landing — two parallel
flights running in opposite directions, joined by a landing and separated by a gap (well
width) between them — from an existing floor opening. The formulas are the same as the
L-shaped stair calculator (both share the same underlying flight-geometry logic and the
same "opening-first" model), but here there is an extra well-width parameter, and the
landing's width is always computed from the flight width and well, never entered directly.
Like the other stair calculators, this is a design (kind: 'design') calculator — the
result is the drawing itself.
Formula
The opening is the primary given, not a result — the same principle as the L-shaped and straight-stair calculators:
total riser count = round(total rise / preferred riser height)
riser height = total rise / riser count (one division for the whole stair, exactly)
lower flight riser count = round(landing height target / riser height)
upper flight riser count = total riser count - lower flight riser count
Going is applied UNIFORMLY to both flights, and its source depends on openingExactFit:
if openingExactFit = false (DEFAULT since 2026-08-23):
going = your own `going` input
upper flight run = going × upper flight tread count
if openingExactFit = true (opt-in):
upper flight run = opening length - nosing overhang
going = upper flight run / upper flight tread count
lower flight run = going × lower flight tread count (the same going, from above)
The lower flight has no opening of its own, so its own run simply inherits the same going.
Why the default changed: in real practice a floor opening is normally cut LARGER than
the upper flight and the flight fixed against one edge of it — an opening that exactly
matches the upper flight's own length is the rarer case, not the general one. So
openingExactFit = false (the new default) treats going as a real, independent design
input — the opening length plays no part in sizing either flight. openingExactFit = true
remains available for the case where the opening genuinely was cut to the upper flight's own
size — then the opening genuinely sets the going and the whole upper-flight run, as
described below; the comfort formula remains only a separate diagnostic that checks the
result, not its source, either way.
Why the nosing overhang is subtracted (openingExactFit = true path only): every
tread's own nosing projects forward past its own riser line by exactly noseOverhang —
including the FIRST tread of the upper flight, whose nosing projects past the flight's own
starting point itself. If the upper flight's run were set to exactly the opening's own
length (no subtraction), the opening's near edge would always coincide with the flight's own
starting point, and the first tread's nosing would always sit just outside the opening's own
boundary — no matter how the opening was sized or positioned. That was a modelling bug, not
physics (see the L-shaped stair calculator's own Formula — identical problem and fix, since
both calculators share the same underlying flight-geometry logic).
opening's near edge (from the landing) = upper flight run - opening length (may be negative)
The upper flight's top (the point furthest from the landing, where it reaches the true top
floor) must always land at the opening's far edge. Under openingExactFit = false (the
default), this is typically well negative — a real opening is normally cut larger than the
flight, so going (a real input, not derived from the opening) usually produces a run
shorter than the opening, meaning the flight's own base already sits inside the opening's
own footprint. Under openingExactFit = true (opt-in), the upper flight's run is, by
construction, always equal to the opening length minus the nosing overhang, so the opening's
near edge is always exactly minus the nosing overhang (e.g. -25 mm with the default
25 mm overhang) — not zero — exactly enough for the first tread's own nosing to fall inside
the opening.
landing depth (direction of travel) = flight width (if equal) or your own entered value
landing width (perpendicular to travel) = flight width × 2 + well width (always derived, never entered)
landing height (actual) = sum of the lower flight's own risers
tread depth (incl. nosing) = going + nosing overhang
pitch angle = atan2(riser height, going)
each flight's stringer length = √(that flight's rise² + that flight's run²)
Unlike the L-shaped stair, the two flights here are parallel and run in opposite
directions, not perpendicular to each other — separated by the well width. The landing's
width is never entered directly — it always equals flight width × 2 + well width, so it
can never structurally be narrower than a single flight.
Headroom is checked along the whole climbed path — every tread of the lower flight, the landing itself, and every tread of the upper flight, not just the upper flight. Each point has its own plan footprint and its own height; it is checked against one flat ceiling line (measured from the base floor) and against the opening rectangle — a point is exempt from the check only if its footprint falls entirely inside the opening, not merely overlapping it.
The landing depth check (IRC R311.7.6, the same wording as the L-shaped stair) applies
to the landing's depth, not its width — since the width (always 2×flight width+well)
can never structurally be narrower than a single flight, this check would not mean anything
for it.
The step-comfort (2×riser+going) and pitch-comfort (30-45°) checks use the same criteria and the same two standards as the other two stair calculators.
Worked example
Defaults: opening length 3665 mm, opening width 1100 mm, total rise 3500 mm, stair width
900 mm, well width 100 mm, floor structure thickness 300 mm, required headroom 2000 mm, top
tread flush with the floor, landing depth equal to stair width, landing height target
1050 mm, preferred riser height 175 mm, going 280 mm (a real input, openingExactFit = false), tread thickness 32 mm, nosing overhang 25 mm.
total riser count = round(3500 / 175) = 20 (divides evenly)
riser height = 3500 / 20 = 175 mm (exactly)
lower flight riser count = round(1050 / 175) = 6
upper flight riser count = 20 - 6 = 14
upper flight tread count = 14 - 1 = 13 (top tread flush with the floor)
going = 280 mm (a real input, opening length plays no part)
upper flight run = 280 × 13 = 3640 mm
opening's near edge = 3640 - 3665 = -25 mm
lower flight tread count = 6 - 1 = 5 (the landing itself is the last "tread")
lower flight run = 5 × 280 = 1400 mm
landing depth = 900 mm; landing width = 900 × 2 + 100 = 1900 mm
landing height = 6 × 175 = 1050 mm (exactly matches the target)
tread depth = 280 + 25 = 305 mm
pitch angle = atan2(175, 280) ≈ 0.5586 rad ≈ 32.01°
lower stringer length = √(1050² + 1400²) = 1750 mm (exactly)
upper stringer length = √(2450² + 3640²) ≈ 4387.72 mm
For these inputs the calculator returns exactly this: riser height 175 mm, going 280 mm, tread depth 305 mm, landing width 1900 mm, landing height 1050 mm, lower stringer length 1750 mm, upper stringer length ≈4387.72 mm, 6 risers in the lower flight and 14 in the upper.
Headroom: for these defaults the calculator passes cleanly with no warnings or errors at all — the smallest clearance (2150 mm, a 150 mm margin over the required 2000 mm) is found at the LANDING ITSELF, not the upper flight: since the opening's near edge is now exactly -25 mm (see Formula), the opening fully covers the first upper tread's own nosing, so the whole upper flight is exempt from the check. 2×riser+going = 2×175+280 = 630 mm — exactly DIN 18065's own target, dead centre of the comfort band.
The separate, upper-flight-only "what opening size would this need" answer returns a start of -25 mm and a length of 3665 mm — exactly matching the actual opening length, since these defaults were chosen so the opening exactly satisfies this isolated, upper-flight-only requirement. It evaluates only the upper flight's own need, in isolation from the whole-path check above, and doesn't have to match it.
Timber list: 18 treads (5 in the lower flight + 13 in the upper) × 900 mm wide × 305 mm deep tread boards — 4 boards of 4800 mm. 18 riser boards (out of 20 risers total — 2 risers, which top the landing/floor rather than a tread, are skipped because their board height is smaller than the floor structure thickness): also 4 boards. 4 stringers (2 lower at 1750 mm, 2 upper at ≈4387.72 mm, ≈12,275.44 mm in total): 3 boards.
FAQ
Why isn't the going derived from the 2×riser+going formula any more?
Because that was an earlier, not-yet-fully-applied cut of the "opening-first" model — the
opening was only checked back then, not genuinely used to size the going. By default today
(openingExactFit = false), going is a real input instead; the comfort formula remains
only a separate diagnostic that checks the result. Switching openingExactFit on derives the
going (and the whole upper-flight run) directly from the opening length instead.
Why is the landing checked together with the flights, not separately? Because the headroom check now covers the whole climbed path — every tread of the lower flight, the landing itself, and every tread of the upper flight — compared against one shared ceiling line, not just the upper flight. The opening only exempts points whose whole floor-plan footprint falls inside it.
Why can the opening's near edge be negative?
Because the upper flight's top must always land at the opening's far edge — a structural
requirement. With the default (openingExactFit = false) values this edge is typically well
negative, since a real opening is normally cut larger than the flight and going (a real
input) usually produces a run shorter than the opening — see Assumptions and limits for why
that reach does NOT usefully extend to relieving the landing here, unlike the L-shaped stair
calculator. With openingExactFit = true, this edge is always exactly minus the nosing
overhang (e.g. -25 mm with the default 25 mm overhang) — not zero — since the upper flight's
run is then, by construction, the opening length minus the nosing overhang (see Formula): the
first tread's own nosing needs exactly that much room in the opening before the flight's own
starting point.
What is the well width (wellWidth) for? It is the gap between the two parallel flights, wide enough for a handrail on each side of an open well. This value has no cited standard — none of the three cited standards (DIN 18065, STR 2.02.01:2004, IRC R311.7.6) states a minimum well width directly.
Why is there no landing width check, only a depth check?
Because the landing's width (perpendicular to the direction of travel) is always computed as
2×flight width+well width — it can never structurally be narrower than a single flight, so
the IRC R311.7.6 check applies only to the landing's depth, which can end up smaller if a
manual value is chosen.
Does turnDirection change any computed number here?
No — it is purely a drawing parameter, deciding which side of the well the lower flight sits
on. Both turnDirection and stairPositionInOpening are expressed from the climber's own
left/right hand, standing at the base of the flight before the first step — not a drawing
axis.
Assumptions and limits
meta.standards cites DIN 18065, STR 2.02.01:2004 (for the 2×riser+going comfort check,
with the same 630/650 mm disagreement), and IRC R311.7.6 (for the landing depth check). The
30-45° pitch comfort band remains an uncited general rule. The well width's default and
bounds are this calculator's own choice — none of the three cited standards directly states
a minimum well width.
The required headroom is a user input with no cited minimum, applied along the whole climbed
path — the lower flight, the landing, and the upper flight.
requiredOpeningStart/requiredOpeningLength answer only "what opening size would suffice
for the upper flight" — they do NOT reflect the whole-path check's own result, and may show
that the opening you have is sufficient even when the overall check returns an error for the
landing or lower flight. requiredOpeningStart can also be NEGATIVE — that is a real answer,
not an invalid one: it means the opening would need to start somewhat before the flight's own
starting point (to give the first tread's own nosing the headroom it needs), not that the
requirement is already met right from the flight's own base.
The defaults (opening length, total rise, landing height target) were chosen so the default stair clears its own headroom check with real margin (see Worked example), not merely on a technicality — an earlier version of this calculator shipped with defaults that opened on an error. This doesn't weaken the check itself: any combination where the landing sits high enough relative to the total rise can still fail it — that remains a real, not an artificial, constraint.
Unlike the L-shaped stair calculator, raising the landing height target does NOT pay off
here — verified directly, not assumed: this calculator's landing spans the WHOLE stairwell
width (both flights plus the well — stair width×2 + well width = 1900 mm at these
defaults), not one flight's own width (900 mm), and that landing width sits on the SAME axis
(stairPositionInOpening, X here) the opening's own width also occupies — unlike the
L-shaped stair, where the lower flight (and the landing's own extent) is on a genuinely
PERPENDICULAR axis the opening can never reach regardless of width. For this calculator, an
opening widened enough to span the landing's full 1900 mm ends up ALSO spanning the lower
flight's own band, since the two sit side by side on that same axis, separated only by the
well — there is no realistic intermediate opening that covers the landing but not the lower
flight. Confirmed by direct test: openingWidth widened to 1900 mm or more with
stairPositionInOpening = 'right' DOES clear every error — but only because it exempts the
lower flight too, i.e. removes the ceiling from the ENTIRE stair, not a "cut a bit larger
than the flight" opening any more. That is not the general case this default change is
modelling, so it is not adopted as the default.
landingHeight (1050 mm) and openingLength (3665 mm) are therefore UNCHANGED from the
previous version — and need no change: at these exact numbers, going under the new default
(a real 280 mm input) comes out identical to what the OLD exact-fit derivation already
produced here (split(3665-25,13) = 280 mm exactly), so the upper flight's run and the
opening's near edge land on the same values either way, and the result is byte-identical to
before (riser height 175 mm, going 280 mm, 2×riser+going = 630 mm, zero warnings or errors, a
single informational note, a 150 mm headroom margin) — see compute.test.ts. This
calculator's own landing-height ceiling stays at 30% of the rise, genuinely different from
the L-shaped stair calculator's 35% — a real geometric difference between the 90-degree and
180-degree cases, not an oversight.
One floor-structure-thickness input serves both the upper floor structure and the landing's own construction thickness — the same assumption as the L-shaped stair calculator.
This calculator uses the same shared flight-geometry logic as the L-shaped stair calculator
(buildFlightGeometry, parallelFlightBandLayout, planRectFullyCoveredBy, and related
helpers from packages/geometry), so both calculators' riser/tread formulas, timber
requirement, and parts[] structure are identical — only the landing geometry and the
flights' plan orientation differ (parallel, opposite directions here; perpendicular in the
L-shape). The timber board count is a simple quantity division by the standard board length,
not an optimized cutting plan.