Skip to content
Nnd1106Public

About

STRATLAB — strategic crisis scenario simulator: PESTEL/Porter/SWOT briefing + 4-round cross-functional war-room decisions

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

14 Commits

Folders and files

Repository files navigation

STRATLAB

An interactive browser-based simulation that models strategic decision-making under uncertainty across finance, marketing, operations, and HR.

Pick an industry and a historical (or hypothetical) crisis, get a briefing built from PESTEL, Porter's Five Forces, and SWOT, then run four rounds of cross-functional decisions while a live scorecard, a counterfactual engine, and a procedurally generated situation-report ticker track what your choices actually did — and what the modeled-best alternative would have done instead.

Live demo: https://nnd1106.github.io/strat-lab/

You don't predict the crisis. You decide what happens next.

At a glance

6 industries (Oil & Gas, Commercial Banking, Airlines, Consumer Retail, Automotive Manufacturing, Technology & SaaS) × 6 crisis scenarios (2008 GFC, Strait of Hormuz closure, 1929-style Depression, a pandemic shock, the 1973 oil embargo, a trade war) × 4 decision rounds × 4 functions (Marketing, Operations, HR, Finance), each with 4 industry-specific choices (37 distinct lever definitions in total: 13 reflavored across every industry, plus 24 signature moves exclusive to one industry each).

Architecture

STRATLAB is five layers, each reading only from the layer below it:

  1. Industry / scenario data (data_gen.py → data.js) — the single source of truth. Every industry's baseline scorecard and shock sensitivities, every scenario's shock channels and 4-round intensity curve, every lever's effects, and the real-world-scaled KPI baselines used for translation. No logic lives here, only content.
  2. Framework briefing layer (buildFrameworkContent, renderBriefing) — turns the industry+scenario pairing into a specific PESTEL / Porter's Five Forces / SWOT read, computed from which shock channels that industry is actually most exposed to, not a generic template.
  3. Decision engine (computeRoundOutcome, simulateRound) — applies the scenario shock plus your four chosen levers' effects (and last round's lagged next_round effects) to the six tracked metrics, with diminishing returns on repeated lever choices. This function is pure with respect to global state — it's the one place round math happens.
  4. Impact / translation engine (computeRealMetrics, KPI dashboard, Industry Benchmark, Risk Exposure gauge) — turns the six abstract metrics into industry-realistic figures (revenue in $B, EBITDA margin, headcount, Brand NPS, etc.) purely for display. Nothing here feeds back into the decision engine.
  5. Scoring / counterfactual engine (scoreOption, replaySimulation, Strategist DNA, the Counterfactual Lab) — every round, silently scores every unchosen option through the same decision engine (layer 3), not a second model, weighted toward protecting metrics closest to their floor. The Counterfactual Lab takes this further: it actually re-runs computeRoundOutcome forward through the rest of the game with one substituted choice, rather than estimating the effect.

Methodology

Each round, two things happen to the six tracked metrics (Revenue, Market Share, Cash, Morale, Brand, CSAT):

  1. The scenario shock. The scenario pushes on up to six channels (demand, cost, credit, sentiment, supply chain, commodity) at an intensity set by that round's point on a 4-round curve. Each channel is scaled by the industry's sensitivity to it, mapped to metrics with a fixed weighting, and divided down so no single round can wipe the scorecard.
  2. Your four choices. Each chosen option's effects apply directly. If it isn't round 1, last round's four choices also apply their next_round effects now — the lag that makes some strategies pay off, or hurt, a beat later than you made them. Reusing the same lever option across rounds triggers diminishing returns (2nd use = 50% effects, 3rd+ = 25%, locked in at the moment of choice and reused for its lagged effect too), so repetition can't stack unlimited value.

Every round, the engine also scores every option you didn't pick, under identical conditions, weighted so metrics closer to their floor count for more — this is what powers the Risk Exposure gauge, the Strategist DNA dimensions, and the interactive Counterfactual Lab in the debrief, without requiring a second simulation model.

The KPI dashboard, Industry Benchmark, and radar/trend charts all read from the same six abstract metrics but display them differently: KPIs scale a per-industry real-world baseline by the metric's ratio to its own baseline; charts normalize every metric to a shared fixed display range so a bank's $20 cash baseline and an airline's $6 one are visually comparable.

The narrative interpretation layer

Everything past this point re-presents data the engine already computes — nothing here is a new statistic, and nothing feeds back into the simulation.

  • "Why?" explanations. Every metric delta with a clean, honest trace to one abstract metric (in the war room's Live Scorecard and Key Metrics panel, and the debrief's Company Trajectory) gets a "Why? ⓘ" toggle. It un-sums the same shock-channel and lever-effect math the engine already ran to name which lever(s) and which scenario channel moved that metric most, e.g. "Cash rose primarily from Raise Debt (+7), partially offset by the credit-channel shock from this scenario (−2)." Derived/damped KPI fields (EBITDA margin, Debt/Equity, headcount, attrition) intentionally don't get a toggle — explaining a compound figure via one lever's raw effect would misstate units, so those are left unexplained rather than fabricated.
  • Decision Attribution. Recomputes, for every one of the 16 decisions, how far its score sat above or below the average of its other candidates that round (distinct from the Counterfactual Lab's chosen-vs-best gap) — surfaced as the single biggest positive and negative across the run.
  • Strategic Trade-off Matrix. A scatter of all 16 choices vs. all 16 modeled-best alternatives: X = risk (variance of an option's own effect deltas across the 6 metrics — a concentrated big swing scores higher-risk than the same total spread evenly), Y = the counterfactual engine's composite score. The one-line takeaway compares cluster centroids, not a fixed narrative.
  • Strategist DNA. 6 dimensions (Financial Discipline, Risk Management, Growth Orientation, Operational Thinking, People Strategy, Adaptability), each a formula over already-tracked data — Adaptability, for instance, is literally the average diminishing-returns discount actually applied across all 16 picks, so it directly rewards not repeating a lever into the penalty.
  • Debrief narrative order. Restructured into 8 sections (Executive Verdict → Strategic Scorecard → Strategist DNA → Company Trajectory → Decision Attribution → Counterfactual Lab → Strategic Blind Spot → Lessons for Next Time) so it reads top-to-bottom as a story instead of a stack of panels. The Blind Spot and Lessons text are built the same way as everything else — a small rule-based template keyed off the actual DNA scores and Decision Attribution results, not per-scenario copy, so it holds up across all 36 industry×scenario combinations.

The Executive Report PDF

"Download Executive Report" (debrief screen) generates a full, designed report client-side via jsPDF (cdnjs) — a full-bleed dark cover page (hero photo + gradient, a metadata block, a Board Confidence score with a per-round sparkline and a Board Verdict tag), a one-page Bottom Line summary, a Table of Contents, and 15 numbered content sections on a light background: Executive Verdict, KPI Bridge, Credit Rating & Outlook, Strategic Scorecard, Company Trajectory, Decision Attribution (including the full 16-decision table against the modeled-best alternative), Strategic Patterns, Risk & Stakeholders, Your Path vs. Optimal Path (a featured counterfactual re-simulated via replaySimulation() and presented as an explicit causal chain — decision → immediate effect → next-round effect → cumulative impact → alternative → what it would have protected), Round-by-Round Summary, Strategic Blind Spot, Lessons for Next Time, Final Key Metrics, the full situation log + alternative callouts, and a Methodology & Glossary appendix. Every chart is embedded as a boxed, captioned image (chart.toBase64Image()); every number — including every v7 addition below — is recomputed from the same tracked state the on-screen debrief uses, not invented for the PDF.

Board Confidence starts at 100, loses 10 points per round that scored CAUTION and 20 per round that scored DISTRESS (the same per-round Strategic Health tier the war-room badge already computes), floored at 0 each round so the cover sparkline never dips below zero. The qualitative read and the Board Verdict tag (CONTINUE / CONTINUE WITH CONDITIONS / REASSESS STRATEGY / URGENT INTERVENTION at fixed 80/60/40 score cutoffs) are both picked from that same score, not written per scenario.

Credit Rating bands final Cash Runway, Debt/Equity, and EBITDA Margin against fixed thresholds (9 months, 1.0x, 15%): all three passing is "A — Investment Grade," two is "BBB — Stable," one is "BB — Speculative," none is "B — Distressed." Outlook compares the Round 3 and Round 4 values of the same two KPIs. KPI Bridge and Risk Register reuse the same "named, fixed threshold" approach — see the report's own Methodology appendix for every exact cutoff, and CREDIT_RATING_THRESHOLDS, KPI_BRIDGE_ASSESSMENT_BAND, RISK_SEVERITY_MEDIUM/_HIGH in app.js for the code. Note that jsPDF's standard fonts can't render emoji or most Unicode arrows, so status indicators that read as 🟢/🟡/🔴 or ↑/↓/→ in the brief are implemented as color-coded words instead (e.g. "Ahead" / "Within Range" / "Behind"; "Worsening" / "Flat" / "Improving").

jsPDF ships only Helvetica/Times/Courier — there's no IBM Plex in a PDF without embedding font files as a new asset dependency, which the stack intentionally avoids. Helvetica and Courier stand in for Plex Sans and Plex Mono, keeping the "no new dependencies beyond what's already loaded" constraint intact.

One real bug worth noting: several sentence-builder functions call escapeHtml() on lever labels like "HR & People" so their output is safe to inject via innerHTML on screen (where the browser decodes & back to & automatically). jsPDF has no such decoding step, so that same text printed literally as &. Fixed with decodeHtmlEntities() — sets innerHTML on a scratch element and reads textContent, which both strips tags and decodes entities using the browser's own parser — wired into every text-drawing helper in the PDF generator, not just the section where the bug was first spotted.

Limitations

  • Deterministic, not probabilistic. Given the same choices in the same order, the engine always produces the same outcome — there is no randomness, no dice roll, no hidden variance. What you see in the Counterfactual Lab is an exact re-run, not a sampled estimate.
  • Illustrative shock magnitudes, not calibrated forecasts. Every shock channel value, sensitivity multiplier, and lever effect is directionally grounded in what actually happened in the referenced historical episode, but the specific numbers were chosen to make a 4-round game legible and playable, not fit to real data. The same applies to the KPI dashboard's real_metrics baselines (revenue scale, margins, headcount) — they're directionally realistic for each industry's rough scale, not sourced from any specific company's filings.
  • No real company or financial data. Nothing in this simulator is scraped, sourced, or calibrated from an actual company, market, or dataset. Every number is either hand-set in data_gen.py or derived from those hand-set numbers by a documented formula.
  • A single composite score, not a full decision-theoretic model. The counterfactual scorer's "best alternative" is a simple weighted-sum composite across six metrics — useful for comparing options within one function in one round, not a claim about optimal strategy in general.

Nothing here should be read as a prediction, a risk model, or investment or business advice.

Future work (considered, deliberately deferred)

  • Difficulty levels. A single fixed shock/lever balance was chosen deliberately so every playthrough of a given industry+scenario is comparable and the counterfactual scorer stays meaningful — variable difficulty would mean the "optimal path" means something different every game.
  • Uncertainty ranges / probabilistic outcomes. Sampling shock or effect magnitudes from a distribution would make the game more "realistic" but would break the Counterfactual Lab's exact-replay guarantee, which depends on the engine being deterministic. Kept out on purpose, not missing by oversight.

Stack

Vanilla HTML/CSS/JS + Chart.js + jsPDF (both via cdnjs). No framework, no bundler, no backend. All content lives in data_gen.py and compiles to data.js — a plain const STRATLAB_DATA = {...} global loaded before app.js — so content is versioned independently of UI code and the deployed app never makes a fetch/XHR call of any kind.

strat-lab/
  data_gen.py    # single source of truth for all content — edit this
  data.js        # generated output, committed (regenerated by CI on push)
  index.html     # app shell, 4 screens
  style.css      # war-room / decision-terminal design system
  app.js         # decision engine, impact engine, scoring/counterfactual engine, UI
  assets/        # hero image
  .github/workflows/deploy.yml

To regenerate data.js after editing data_gen.py:

python3 data_gen.py

GitHub Actions re-runs this on every push (in case data_gen.py changed) and deploys the result to Pages — see .github/workflows/deploy.yml.


Built by Niranjan Desai.

About

STRATLAB — strategic crisis scenario simulator: PESTEL/Porter/SWOT briefing + 4-round cross-functional war-room decisions

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages