Quiescence Search fixes a specific failure mode of any depth-limited search: Iterative Deepening's own heuristic evaluator gets called the instant the search budget runs out, wherever the cutoff happens to land — and if it lands in the middle of a forced trade, the number it reports can be badly misleading. A position that's mid-capture looks nothing like a position that's actually settled: material is temporarily up or down by whatever was just taken, and the recapture that would even things out is sitting one ply past the horizon, invisible to a static evaluator that only ever looks at the board in front of it. Quiescence search's fix is narrow and cheap: at a cutoff, don't evaluate immediately — keep searching, but only through capturing moves (the "loud" ones), until the position reaches a point with no capture left on the table at all (a quiet position), and only then hand it to the static evaluator. Every other page on this site's Game Trees list either decides a move outright or caches/keys a position it's already scored; this one decides something narrower still — whether a leaf is trustworthy enough to evaluate yet.
The five boxes below model one square under a chain of forced captures — the simplified case quiescence search exists for (real engines call the full version of this static exchange evaluation). At each ply the side to move can either stand pat — decline the capture, banking the material total exactly as it stands — or capture, changing the running total by that ply's value and handing the same choice to the other side. Pick a nominal search depth, then compare what a plain depth-limited search reports against what quiescence search reports for the identical budget.
fixed-depth search — horizon cuts off after the selected depth
quiescence search — keeps following captures regardless of the selected depth
Both searches share the exact same shape: at every ply, the side to move picks whichever is better for them, standing pat (keep the current total) or capturing (recurse one ply deeper with the total updated). White maximizes that choice, Black minimizes it — the same alternation as Minimax. The only difference is what's allowed to stop the recursion. Fixed-depth search stops on two conditions: the chain runs out of captures (quiet), or the nominal depth budget hits zero — whichever comes first, no matter which one it is. Quiescence search drops the second condition entirely for capturing moves: the budget still governs how deep a quiet continuation gets searched, but a capture is never refused just because the counter hit zero. The chain above only has one kind of continuation (another capture) until it runs out at ply 5, so quiescence search always plays it out to the real end regardless of the selected depth, landing on the same value a full, unlimited search would find.
The stand-pat option is what keeps that value honest. Without it, a side would be forced to capture
whenever one is available even when doing so makes their own position worse — and the true, correctly
computed value of this chain (+1, checked directly with the widget above at depth 5) shows
exactly why that matters: White wins the first pawn (+1), and Black's own best response,
computed by the same recursion, is to decline the recapture rather than walk into a
losing continuation three plies deeper. A search without a stand-pat option can't represent that choice
at all — see Pitfalls below for exactly how far off the answer gets.
// captures[i].delta is the running-total change if ply i's capture is made;
// White moves on even plies, Black on odd. Both functions return the eval
// from White's perspective, with each side free to decline (stand pat).
function search(idx, evalBefore, depth) {
if (idx >= captures.length) return evalBefore; // quiet — nothing left to capture
if (depth === 0) return evalBefore; // horizon — trust the static number here
const evalAfter = evalBefore + captures[idx].delta;
const continued = search(idx + 1, evalAfter, depth - 1);
const white = idx % 2 === 0;
return white ? Math.max(evalBefore, continued) : Math.min(evalBefore, continued);
}
function quiesce(idx, evalBefore) {
if (idx >= captures.length) return evalBefore; // quiet — same stopping condition as search()
const evalAfter = evalBefore + captures[idx].delta;
const continued = quiesce(idx + 1, evalAfter); // depth never gates a capturing move
const white = idx % 2 === 0;
return white ? Math.max(evalBefore, continued) : Math.min(evalBefore, continued);
}
That's the whole algorithm: quiesce is search with the horizon check
deleted. A real engine's quiescence search adds two things this toy chain doesn't need to show its
point: alpha-beta pruning on top (the exact same guarantee established on Minimax's own page — an identical answer for less work, never a
different one), and, because captures alone are guaranteed to terminate (there's only finitely much
material on a board) but checks aren't, most real implementations also cap how many plies quiescence is
allowed to extend through checks specifically, as a safety net against a forced-check line that never
naturally quiets down.
A fixed depth's evaluation doesn't degrade gracefully — it flickers between right and wrong
depending on exactly where the horizon lands. Checked directly with the widget above across
every depth from 1 to 5: the fixed-depth eval comes back +1, 0, +1, 0, +1 — correct at odd
depths, wrong at even ones, alternating for no reason a user watching only the final number would
guess. Depth 2 happens to cut off right after Black's recapture, which looks like a fair trade
(0) from that snapshot alone; a search that stopped there has no way to know Black
wouldn't actually make that recapture in real play, once White's own follow-up three plies later is
visible. This isn't a case of "deeper is always more accurate, just costlier" — depth 4 is a strictly
deeper search than depth 3 and returns a worse answer, purely because of where its particular
horizon happens to fall in this exchange. Quiescence search returns +1 at every one of the
same five depth settings, because it never lets the horizon fall mid-capture in the first place.
Drop the stand-pat option and the answer isn't just off — it's off by the width of the
entire exchange. Checked directly against a variant of the reference implementation above with
the Math.max(evalBefore, continued) / Math.min(evalBefore, continued) stand-pat
comparison removed (each side forced to capture whenever one is on the table): the chain plays
out to its very end no matter who's "winning" along the way, returning +5 — the sum of
every delta in the chain — instead of the true +1. That's not a rounding error; it's
claiming White is up a rook-and-a-half more than they actually are, because the forced version can't
represent Black ever declining a bad trade.
Quiescence search only ever extends through captures — it doesn't rescue every kind of
horizon blindness. Both functions above have no branch at all for a quiet developing move, a
positional threat, or a slow-building attack that never involves a capture; quiesce's
stopping condition is "no capture left," not "nothing left that matters." A quiet tactical idea sitting
just past the horizon is exactly as invisible to quiescence search as it is to plain fixed-depth
search — the fix is narrow by design, aimed specifically at the forced-exchange instability demonstrated
above, not at horizon effects generally.
Time: on this page's own five-ply chain, fixed-depth search visits exactly
depth + 1 nodes (checked directly: 2, 3, 4, 5, 6 nodes for depths 1 through 5), and
quiescence search visits 6 — the full chain plus its final quiet check — regardless of
the selected depth, since nothing here ever gates a capture. The chain is deliberately a straight line
(one capture opportunity per ply, no branching) to isolate the stand-pat and horizon mechanics without
anything else moving at the same time; a real position usually has more than one legal capture
available at a given ply. Generalized, quiescence search costs O(c^k), where
k is how many plies of forcing captures lie along a line and c is the
branching factor restricted to capturing moves only (c ≤ b, the full branching
factor Minimax's own Complexity section uses) — cheap next to
extending a full O(b^d) search that same k plies deeper, precisely because
most legal moves in a real position are quiet, not captures. Space: O(k)
for the recursion stack, the same single-line-of-play footprint as every other entry in this
category.
This is the site's eighth Game Trees entry, and like Transposition Tables and Zobrist Hashing it doesn't decide a move by itself — it decides something one level removed from that. Where Transposition Tables decides whether a position is worth re-deriving and Zobrist Hashing decides how cheaply to name one, this page decides whether a position reached at the search horizon is settled enough to trust a static evaluator's number at all. It pairs most directly with Iterative Deepening, the entry that introduced this site's own heuristic-at-cutoff idea in the first place — quiescence search is what keeps that heuristic from being asked to judge a position mid-exchange, exactly the gap Minimax's own fixed-depth board never has to worry about, since its four-empty-cell demo always searches all the way to a genuine win, loss, or draw. See Choosing a Game Tree Search Algorithm for how all ten compare side by side.