<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: connerlambden</title>
    <description>The latest articles on DEV Community by connerlambden (@connerlambden).</description>
    <link>https://dev.to/connerlambden</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3776793%2F2b4714ed-0e1b-407a-a636-4236e644b524.jpeg</url>
      <title>DEV Community: connerlambden</title>
      <link>https://dev.to/connerlambden</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/connerlambden"/>
    <language>en</language>
    <item>
      <title>How to improve your IQ (without lying to yourself)</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Mon, 20 Jul 2026 16:54:42 +0000</pubDate>
      <link>https://dev.to/connerlambden/how-to-improve-your-iq-without-lying-to-yourself-49da</link>
      <guid>https://dev.to/connerlambden/how-to-improve-your-iq-without-lying-to-yourself-49da</guid>
      <description>&lt;p&gt;Search demand for "how to improve your IQ" and "how to improve your intelligence" usually wants one of three different things, and sellers often blur them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three claims people mix up
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test score movement.&lt;/strong&gt; Practice effects, item familiarity, sleep, and anxiety can move an IQ score without proving a durable jump in general intelligence (g).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Competence growth.&lt;/strong&gt; Crystallized skill and domain expertise keep rising for years with hard study. That is real improvement even when Raven-like fluid scores barely budge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Far transfer from brain games to broad Gf.&lt;/strong&gt; Near transfer to trained tasks is common. Large lasting Gf gains are not the default finding once active controls enter the picture.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If an article promises a free upgrade to general intelligence from puzzles alone, shrink the claim until it matches the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  A boring adult plan that survives contact with research
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sleep and aerobic exercise first.&lt;/li&gt;
&lt;li&gt;Deliberate practice in a hard domain (math, language, code, music).&lt;/li&gt;
&lt;li&gt;Optional adaptive reasoning practice if you enjoy it.&lt;/li&gt;
&lt;li&gt;Treat any practice estimate as training feedback, not a clinical IQ diagnosis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources I maintain
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Claim-size map: &lt;a href="https://intelligencemax.ai/guide" rel="noopener noreferrer"&gt;intelligencemax.ai/guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Evidence pack: &lt;a href="https://osf.io/kja9b/" rel="noopener noreferrer"&gt;osf.io/kja9b&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclosure: I build IntelligenceMax, an adaptive reasoning gym. Practice software is not a promise that apps raise trait IQ.&lt;/p&gt;

</description>
      <category>psychology</category>
      <category>learning</category>
      <category>evidence</category>
    </item>
    <item>
      <title>Lumosity, Elevate, Peak: same aisle, different drills</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Thu, 16 Jul 2026 21:06:32 +0000</pubDate>
      <link>https://dev.to/connerlambden/lumosity-elevate-peak-same-aisle-different-drills-2e04</link>
      <guid>https://dev.to/connerlambden/lumosity-elevate-peak-same-aisle-different-drills-2e04</guid>
      <description>&lt;p&gt;Search demand lumps Lumosity, Elevate, Peak, BrainHQ, and NeuroNation into one bucket: "brain training apps." Product design does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three different product jobs
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Language / productivity drills&lt;/strong&gt; (Elevate-shaped): reading, writing, listening, math fluency. Progress charts track those drills.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognitive mini-games&lt;/strong&gt; (Lumosity / Peak-shaped): memory, attention, flexibility, speed under time pressure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning practice&lt;/strong&gt; (what I ship): adaptive multiple-choice distinctions at your edge, with an honest practice score that is &lt;em&gt;not&lt;/em&gt; a clinical IQ claim.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you evaluate all three with the same KPI ("raises IQ"), you will misread the literature and mis-buy the subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the evidence usually supports
&lt;/h2&gt;

&lt;p&gt;Independent reviews of commercial brain training typically find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Near transfer:&lt;/strong&gt; you get better at the trained tasks and close cousins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Far transfer:&lt;/strong&gt; weak under careful controls for everyday reasoning / general intelligence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful anchors: Simons et al. 2016; Melby-Lervåg, Redick &amp;amp; Hulme 2016 on working-memory training. Pick the app whose drills match the skill you actually want to practice.&lt;/p&gt;

&lt;p&gt;I keep a public claim-size map here: &lt;a href="https://intelligencemax.ai/guide" rel="noopener noreferrer"&gt;intelligencemax.ai/guide&lt;/a&gt; (OSF: &lt;a href="https://osf.io/kja9b/" rel="noopener noreferrer"&gt;osf.io/kja9b&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for builders
&lt;/h2&gt;

&lt;p&gt;If you are shipping adaptive difficulty, do not market "adaptive" as "clinically validated IQ gains." Adaptive means the item bank tracks an ability estimate for &lt;em&gt;your&lt;/em&gt; task family. That is useful. It is still not an IQ test.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://apps.apple.com/us/app/intelligencemax/id6786447264" rel="noopener noreferrer"&gt;IntelligenceMax&lt;/a&gt;, an iOS reasoning gym. Practice estimate ≠ clinical IQ.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick chooser
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Want…&lt;/th&gt;
&lt;th&gt;Prefer…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grammar / reading fluency games&lt;/td&gt;
&lt;td&gt;Elevate-like&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Short attention/memory mini-games&lt;/td&gt;
&lt;td&gt;Lumosity/Peak-like&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hard MCQ reasoning under adaptive difficulty&lt;/td&gt;
&lt;td&gt;Reasoning gym&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same aisle. Different drills. Claim size is the product.&lt;/p&gt;

</description>
      <category>analysis</category>
      <category>product</category>
      <category>productivity</category>
      <category>reviews</category>
    </item>
    <item>
      <title>What "adaptive" should mean in a reasoning practice app</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Thu, 16 Jul 2026 20:58:45 +0000</pubDate>
      <link>https://dev.to/connerlambden/what-adaptive-should-mean-in-a-reasoning-practice-app-3eko</link>
      <guid>https://dev.to/connerlambden/what-adaptive-should-mean-in-a-reasoning-practice-app-3eko</guid>
      <description>&lt;p&gt;Most apps that say adaptive just mean a streak counter and a harder level after three greens.&lt;/p&gt;

&lt;p&gt;I shipped IntelligenceMax on the App Store with a narrower definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adaptive as serving, not as a vibe
&lt;/h2&gt;

&lt;p&gt;After each answer, the app updates a practice estimate and uses that to choose later item difficulty. Explanations appear after submit, not before. That is the loop: miss → see why → get something near that edge again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the score is not
&lt;/h2&gt;

&lt;p&gt;The estimate uses an IRT-shaped update and can be shown on an IQ-looking scale for readability. It is still an in-app practice metric. It is not a normed clinical IQ battery, and shipping to the App Store does not change that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why builders should care about the claim size
&lt;/h2&gt;

&lt;p&gt;App Store categories reward big cognitive verbs. Independent reviews of commercial brain training are usually clearer about near transfer than about far transfer. If your product only proves "people get better at your items," say that. Do not let the marketing department invent adult &lt;em&gt;g&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try / read
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;App Store: &lt;a href="https://apps.apple.com/us/app/intelligencemax/id6786447264" rel="noopener noreferrer"&gt;https://apps.apple.com/us/app/intelligencemax/id6786447264&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Methods: &lt;a href="https://intelligencemax.ai/science" rel="noopener noreferrer"&gt;https://intelligencemax.ai/science&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I build IntelligenceMax. Feedback on explanation quality beats applause for IQ theater.&lt;/p&gt;

</description>
      <category>product</category>
    </item>
    <item>
      <title>I graded my own ML option forecasts. Here's the Brier score.</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 22:24:55 +0000</pubDate>
      <link>https://dev.to/connerlambden/i-graded-my-own-ml-option-forecasts-heres-the-brier-score-20fp</link>
      <guid>https://dev.to/connerlambden/i-graded-my-own-ml-option-forecasts-heres-the-brier-score-20fp</guid>
      <description>&lt;p&gt;In May I promised — out loud, on the internet, where people can screenshot you — that I'd grade Helium's published &lt;code&gt;prob_itm&lt;/code&gt; forecasts when the June 2026 AAPL contracts expired.&lt;/p&gt;

&lt;p&gt;June 26 came. The contracts died. So I graded them.&lt;/p&gt;

&lt;p&gt;This is not a victory lap. Mean Brier loss on &lt;strong&gt;n=2&lt;/strong&gt; is &lt;strong&gt;0.3846&lt;/strong&gt;. A coin flip that always says 0.5 scores 0.25 when the world resolves to 0 or 1. We did worse than a coin flip. That is information.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we froze in May
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contract&lt;/th&gt;
&lt;th&gt;Helium &lt;code&gt;prob_itm&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Market-ish implied&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AAPL $310C 2026-06-26&lt;/td&gt;
&lt;td&gt;0.42&lt;/td&gt;
&lt;td&gt;~0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AAPL $295P 2026-06-26&lt;/td&gt;
&lt;td&gt;0.23&lt;/td&gt;
&lt;td&gt;~0.24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What the stock actually did
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AAPL NASDAQ close on 2026-06-26: $283.78&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$310 call → OTM → realized ITM = 0 → Brier = (0.42 − 0)² = &lt;strong&gt;0.1764&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;$295 put → ITM → realized ITM = 1 → Brier = (0.23 − 1)² = &lt;strong&gt;0.5929&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Honest read (short)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The call forecast was closer to reality than the market's ~0.50 shrug — but "closer" is not "good." Any non-zero probability on an OTM finish still costs you.&lt;/li&gt;
&lt;li&gt;The put is the bruise. Probabilities looked aligned pre-expiry; then the underlying fell hard enough that both market and model had understated the ITM risk. Helium's number was far from 1, so the Brier loss is ugly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;n=2&lt;/strong&gt;. This is a promise kept, not a calibration paper. If you want to treat it as science, wait for more expiries on the &lt;a href="https://connerlambden.github.io/helium-news-explorer/calibration.html" rel="noopener noreferrer"&gt;honesty board&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reproduce
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/connerlambden/helium-mcp-cookbook
&lt;span class="nb"&gt;cd &lt;/span&gt;helium-mcp-cookbook
python calibration/grade_june_expiry.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frozen inputs live in &lt;a href="https://github.com/connerlambden/helium-mcp-cookbook/blob/main/calibration/june_2026_forecasts.json" rel="noopener noreferrer"&gt;&lt;code&gt;calibration/june_2026_forecasts.json&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother publishing a bad score
&lt;/h2&gt;

&lt;p&gt;Because the alternative is the usual ML options blog post: a backtest, a vibe, a screenshot of a green P&amp;amp;L. Publishing &lt;code&gt;prob_itm&lt;/code&gt; with a fixed expiry is a tiny discipline. Grading it after the fact is the second half of that discipline.&lt;/p&gt;

&lt;p&gt;If you log your own forecasts with &lt;a href="https://github.com/connerlambden/helium-mcp-cookbook/blob/main/recipes/02_options_calibration_tracker.py" rel="noopener noreferrer"&gt;recipe 02&lt;/a&gt;, you can run the same scorer when your contracts expire. I'll keep adding rows.&lt;/p&gt;

&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;Also adjacent, if you're here for news rather than options: we published a free &lt;a href="https://huggingface.co/datasets/HeliumTrades/helium-news-bias-corpus" rel="noopener noreferrer"&gt;212×37 news-outlet framing corpus&lt;/a&gt; with an &lt;a href="https://connerlambden.github.io/helium-news-explorer/" rel="noopener noreferrer"&gt;explorer&lt;/a&gt;. Different animal. Same "put numbers where people can grade them" instinct.&lt;/p&gt;

</description>
      <category>python</category>
      <category>machinelearning</category>
      <category>statistics</category>
    </item>
    <item>
      <title>An IRT-shaped practice score is not an IQ test</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 22:10:27 +0000</pubDate>
      <link>https://dev.to/connerlambden/an-irt-shaped-practice-score-is-not-an-iq-test-42c8</link>
      <guid>https://dev.to/connerlambden/an-irt-shaped-practice-score-is-not-an-iq-test-42c8</guid>
      <description>&lt;p&gt;IntelligenceMax updates a learner estimate after every answer and displays it on the familiar IQ scale. The arithmetic resembles item response theory. That resemblance is useful, but it is easy to ask the formula to carry more meaning than it can bear.&lt;/p&gt;

&lt;p&gt;A formula can be internally consistent while its inputs remain uncertain. Here, the distinction begins with the item parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The update rule
&lt;/h2&gt;

&lt;p&gt;For ability θ, item difficulty b, and discrimination a, the model assumes a logistic P(correct). The first estimate has a normal prior centred at 100 with SD 15. After an answer, the implementation takes one capped local update using score and Fisher information. Per-answer change is capped at 1.5 points.&lt;/p&gt;

&lt;p&gt;The calculation is the easy part. The inputs are where the uncertainty lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model-assigned is not empirically calibrated
&lt;/h2&gt;

&lt;p&gt;The AI that generates each question also assigns difficulty and discrimination. Those values are checked for shape and range, but they are not fitted from a norming sample. Calling the update IRT-style describes the logistic form. It does not establish that 115 means the same thing as a score of 115 on a validated instrument.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this number can support
&lt;/h2&gt;

&lt;p&gt;Within those limits, the estimate can support an adaptive practice loop. It cannot establish a clinical Full Scale IQ, a permanent change in general intelligence, transfer to school or work, or treatment of a cognitive condition.&lt;/p&gt;

&lt;p&gt;Reviews of commercial brain training find the strongest evidence on trained tasks and close relatives. Broad gains are less convincing against active controls (Simons et al., 2016; Melby-Lervåg et al., 2016).&lt;/p&gt;

&lt;p&gt;Until empirical calibration exists:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a logistic, IRT-shaped practice estimate that uses model-assigned item parameters. It is not a normed IQ score.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Public notes: &lt;a href="https://intelligencemax.ai/science" rel="noopener noreferrer"&gt;https://intelligencemax.ai/science&lt;/a&gt;&lt;br&gt;
Evidence map: &lt;a href="https://intelligencemax.ai/guide" rel="noopener noreferrer"&gt;https://intelligencemax.ai/guide&lt;/a&gt;&lt;br&gt;
OSF: &lt;a href="https://osf.io/kja9b/" rel="noopener noreferrer"&gt;https://osf.io/kja9b/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disclosure: I built IntelligenceMax. The technical question I most want readers to challenge is whether useful empirical calibration is possible for generated, mostly one-off items without quietly turning the system into a fixed bank.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>datascience</category>
      <category>learning</category>
    </item>
    <item>
      <title>How to improve your IQ without lying to yourself</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 21:02:57 +0000</pubDate>
      <link>https://dev.to/connerlambden/how-to-improve-your-iq-without-lying-to-yourself-15fh</link>
      <guid>https://dev.to/connerlambden/how-to-improve-your-iq-without-lying-to-yourself-15fh</guid>
      <description>&lt;p&gt;People type “how to improve IQ” into Google the way thirsty people type “how to hydrate instantly.” Fair. Also: the internet will sell you a miracle before it sells you sleep.&lt;/p&gt;

&lt;h2&gt;
  
  
  What IQ is (and is not)
&lt;/h2&gt;

&lt;p&gt;IQ is a test score. Useful. Incomplete. Noisy day to day. It is not a moral ranking, a destiny, or a personality.&lt;/p&gt;

&lt;p&gt;If your plan is “raise my number by Friday,” you are shopping for fog. Scores move with sleep, anxiety, practice effects, and which test you took.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually tends to help
&lt;/h2&gt;

&lt;p&gt;Schooling and hard domain practice still look better than most commercial brain-training promises. Exercise and sleep help cognition more reliably than another puzzle leaderboard. Reading hard books and taking feedback that can embarrass you remains undefeated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Near transfer vs far transfer
&lt;/h2&gt;

&lt;p&gt;Near transfer is real: practice a skill, get better at that skill and nearby ones.&lt;/p&gt;

&lt;p&gt;Far transfer — train a game, raise general intelligence forever — is where ads get loud and meta-analyses get quiet. Simons et al. (2016) on commercial brain training. Melby-Lervåg and colleagues on working-memory training. Treat “this app raised my IQ 15 points” with the same skepticism you would bring to a late-night fitness infomercial.&lt;/p&gt;

&lt;h2&gt;
  
  
  A plan that is kinder than chasing points
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pick one hard skill you will actually use.&lt;/li&gt;
&lt;li&gt;Force weekly output that can be graded or rejected.&lt;/li&gt;
&lt;li&gt;Sleep like it is part of the training.&lt;/li&gt;
&lt;li&gt;Revisit IQ only if you need it for something concrete. Do not let it narrate your identity.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Disclosure
&lt;/h2&gt;

&lt;p&gt;I build &lt;a href="https://intelligencemax.ai" rel="noopener noreferrer"&gt;IntelligenceMax&lt;/a&gt;: adaptive reasoning practice. We do not claim it raises IQ. We care about transfer honesty.&lt;/p&gt;

&lt;p&gt;Longer map: &lt;a href="https://intelligencemax.ai/guide" rel="noopener noreferrer"&gt;intelligencemax.ai/guide&lt;/a&gt;&lt;br&gt;&lt;br&gt;
OSF notes: &lt;a href="https://osf.io/kja9b/" rel="noopener noreferrer"&gt;osf.io/kja9b&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Canonical essay: &lt;a href="https://intelmax.substack.com/p/how-to-improve-your-iq-without-lying" rel="noopener noreferrer"&gt;How to improve your IQ (without lying to yourself)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Getting sharper is allowed. Lying to yourself about what transferred is optional, and expensive.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>productivity</category>
      <category>science</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>An IRT-shaped practice score is not an IQ test</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 16:39:53 +0000</pubDate>
      <link>https://dev.to/connerlambden/an-irt-shaped-practice-score-is-not-an-iq-test-42ii</link>
      <guid>https://dev.to/connerlambden/an-irt-shaped-practice-score-is-not-an-iq-test-42ii</guid>
      <description>&lt;p&gt;IntelligenceMax updates a learner estimate after every answer and displays it on the familiar IQ scale. The arithmetic resembles item response theory. That resemblance is useful, but it is easy to ask the formula to carry more meaning than it can bear.&lt;/p&gt;

&lt;p&gt;A formula can be internally consistent while its inputs remain uncertain. Here, the distinction begins with the item parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The update rule
&lt;/h2&gt;

&lt;p&gt;For ability &lt;code&gt;θ&lt;/code&gt;, item difficulty &lt;code&gt;b&lt;/code&gt;, and discrimination &lt;code&gt;a&lt;/code&gt;, the model assumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;P(correct) = sigmoid(a × (θ - b) / 15)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first estimate has a normal prior centred at 100 with a standard deviation of 15. After an answer &lt;code&gt;u&lt;/code&gt;, where correct is 1 and incorrect is 0, the implementation takes one capped local update using the score and Fisher information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;information = (a / 15)² × p × (1 - p)
posterior precision = prior precision + information
delta = (u - p) × (a / 15) / posterior precision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The per-answer change is capped at 1.5 points. Displayed uncertainty also has a floor, including a &lt;code&gt;15 / √n&lt;/code&gt; evidence floor based on the number of answers.&lt;/p&gt;

&lt;p&gt;Suppose the current estimate is 100 with a standard error of 5. A question has &lt;code&gt;b = 115&lt;/code&gt; and &lt;code&gt;a = 1.2&lt;/code&gt;. The assumed probability of a correct answer is about 0.23. A correct response moves the estimate up by about 1.5 points after the cap; an incorrect response moves it down by about 0.45.&lt;/p&gt;

&lt;p&gt;The calculation is the easy part to show. The inputs are where the uncertainty lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model-assigned is not empirically calibrated
&lt;/h2&gt;

&lt;p&gt;The AI that generates each question also assigns its difficulty and discrimination. Those values are checked for shape and range, but they are not fitted from a norming sample. Calling the update "IRT-style" describes the logistic form. It does not establish that 115 means the same thing as a score of 115 on a validated instrument.&lt;/p&gt;

&lt;p&gt;The current system can calculate the answer probability implied by the learner estimate and the assumed item parameters. It does not yet establish that questions labelled 115 are answered at the predicted rate across an appropriate population. That would require empirical item calibration, stable items, enough responses per item, and checks for differential item functioning.&lt;/p&gt;

&lt;p&gt;The product also reports a separate confidence-calibration score. That score is a Brier-style measure comparing a user's stated confidence with correctness. It does not validate item difficulty or the ability estimate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this number can support
&lt;/h2&gt;

&lt;p&gt;Within those limits, the estimate can support an adaptive practice loop. It gives the system a consistent way to react to surprising answers and prefer questions near the current estimate.&lt;/p&gt;

&lt;p&gt;It cannot establish a clinical Full Scale IQ, a permanent change in general intelligence, transfer to school or work, or treatment of a cognitive condition. Those conclusions require validated instruments and, for change claims, a study that separates training from expectation, retesting, selection, and ordinary fluctuation.&lt;/p&gt;

&lt;p&gt;The transfer literature matters here. Reviews of commercial brain training and working-memory training find the strongest evidence on trained tasks and close relatives. Broad gains are less convincing, especially against active control groups. A rising practice estimate may represent learning within the task family. It is not evidence by itself that a distant ability changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would test next
&lt;/h2&gt;

&lt;p&gt;An empirical calibration report should group responses by predicted probability and compare prediction with observed accuracy. A simple first diagnostic would publish reliability bins, sample counts, and a Brier score or log loss for the model's &lt;code&gt;P(correct)&lt;/code&gt;. Stable reusable items would permit item-level residuals. Generated one-off items make that harder and may require hierarchical evaluation by prompt version, domain, and assigned difficulty band.&lt;/p&gt;

&lt;p&gt;Until that work exists, the precise statement is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a logistic, IRT-shaped practice estimate that uses model-assigned item parameters. It is not a normed IQ score.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The current measurement notes are public at &lt;a href="https://intelligencemax.ai/science" rel="noopener noreferrer"&gt;https://intelligencemax.ai/science&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simons et al. (2016): &lt;a href="https://doi.org/10.1177/1529100616661983" rel="noopener noreferrer"&gt;https://doi.org/10.1177/1529100616661983&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Melby-Lervåg, Redick, and Hulme (2016): &lt;a href="https://doi.org/10.1177/1745691616635612" rel="noopener noreferrer"&gt;https://doi.org/10.1177/1745691616635612&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclosure: I built IntelligenceMax. The technical question I most want readers to challenge is whether useful empirical calibration is possible for generated, mostly one-off items without quietly turning the system into a fixed bank.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Store update
&lt;/h2&gt;

&lt;p&gt;IntelligenceMax is now on the &lt;a href="https://apps.apple.com/us/app/intelligencemax/id6786447264" rel="noopener noreferrer"&gt;App Store&lt;/a&gt;. The practice-score limits in this article still apply on iPhone.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>programming</category>
      <category>ai</category>
      <category>learning</category>
    </item>
    <item>
      <title>Open evidence matrix for "get smarter" claims (OSF)</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 05:00:00 +0000</pubDate>
      <link>https://dev.to/connerlambden/open-evidence-matrix-for-get-smarter-claims-osf-188</link>
      <guid>https://dev.to/connerlambden/open-evidence-matrix-for-get-smarter-claims-osf-188</guid>
      <description>&lt;p&gt;Commercial brain training often sells far transfer: practice this game, get smarter in life. Cognitive psychology usually finds something narrower: people get better at what they practice, and generalization is the hard part.&lt;/p&gt;

&lt;p&gt;A usable filter for get-smarter claims:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name the outcome (acute state, skill/knowledge, aging trajectory, measured IQ, or trained-task score).&lt;/li&gt;
&lt;li&gt;Ask what transferred (same task family vs a broader battery with real controls).&lt;/li&gt;
&lt;li&gt;Prefer active controls and pre-registration over testimonials.&lt;/li&gt;
&lt;li&gt;Remove real constraints first (sleep debt, clinical issues, true deficiencies), then build hard skills for competence goals.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I published a small open companion for this filter: an evidence-ranked CSV of interventions related to intelligence / cognitive performance outcomes, plus a citation pack. It is not a systematic review of everything, and it does not claim that any product raises IQ.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OSF project: &lt;a href="https://osf.io/kja9b/" rel="noopener noreferrer"&gt;https://osf.io/kja9b/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Narrative companion: &lt;a href="https://intelligencemax.ai/guide" rel="noopener noreferrer"&gt;https://intelligencemax.ai/guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclosure: I maintain the matrix and also build educational software in the space (IntelligenceMax). Use the OSF files on their own if you only want the CSV.&lt;/p&gt;

</description>
      <category>data</category>
      <category>learning</category>
      <category>productivity</category>
      <category>science</category>
    </item>
    <item>
      <title>Claim Roulette: 12 “get smarter” ads, graded in 12 seconds each</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 03:28:59 +0000</pubDate>
      <link>https://dev.to/connerlambden/claim-roulette-12-get-smarter-ads-graded-in-12-seconds-each-epa</link>
      <guid>https://dev.to/connerlambden/claim-roulette-12-get-smarter-ads-graded-in-12-seconds-each-epa</guid>
      <description>&lt;h1&gt;
  
  
  Claim Roulette: 12 “get smarter” ads, graded in 12 seconds each
&lt;/h1&gt;

&lt;p&gt;A silly format that makes a serious distinction. Each claim gets a letter grade and one verdict. Steal freely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grading scale&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt; — Strong, boring, replicable pathway for a named outcome&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt; — Real effect, wrong marketing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt; — Near transfer / training dressed as magic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt; — Hypothesis cosplaying as product science&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;F&lt;/strong&gt; — Soup marketed as a g vaccine&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Train 10 minutes a day and raise your IQ.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;F&lt;/strong&gt;. IQ batteries are not app streaks. Trained-task gains ≠ general ability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Sleep debt destroys cognitive performance.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;A&lt;/strong&gt; for &lt;em&gt;acute&lt;/em&gt; sharpness. Not a lasting rewire of g. Fix this before you buy anything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Dual n-back will permanently boost fluid intelligence.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;D&lt;/strong&gt; historically loud, now mostly sour. Treat as WM workout, not inoculation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“This nootropic stack raises intelligence in healthy adults.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;F&lt;/strong&gt; unless they name molecule, dose, deficit, and a design that would embarrass them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Learning a hard skill (code / language / instrument) makes you smarter.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;A/B&lt;/strong&gt;. It thickens knowledge and often competence. Saying it “raises IQ” is optional theater.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Brain games prevent Alzheimer’s.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;F&lt;/strong&gt; as a product claim. Disease prevention is a clinical claim with receipts or silence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Processing-speed training helped older adults on trained and some near outcomes.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;B&lt;/strong&gt;. Population and DV matter. Not a youth IQ potion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Chess makes kids smarter.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;C&lt;/strong&gt;. Chess kids are selected and practiced. Far transfer metas are unkind to the slogan.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Exercise helps cognition.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;B&lt;/strong&gt; for many acute/health pathways. Still not “bought 15 IQ points.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Adaptive reasoning practice with honest difficulty is useful deliberate practice.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;B&lt;/strong&gt; as practice. &lt;strong&gt;F&lt;/strong&gt; if sold as clinically proven g boost. (This is where we live — disclosure: IntelligenceMax is that gym.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Iodine deficiency correction can matter a lot where deficiency exists.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;A&lt;/strong&gt; for that population. Marketing to already-nourished Instagram is cargo cult.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;“Our scoreboard went up, therefore intelligence rose.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Grade: &lt;strong&gt;F&lt;/strong&gt;. The scoreboard is the thing that learned.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The honest punchline
&lt;/h2&gt;

&lt;p&gt;Want fewer F grades in your life? Separate &lt;strong&gt;dials&lt;/strong&gt; (g vs knowledge vs acute state vs long-horizon health), demand &lt;strong&gt;active controls&lt;/strong&gt;, and treat “raises intelligence” like a claim that owes you a design.&lt;/p&gt;

&lt;p&gt;Public map: &lt;a href="https://intelligencemax.ai/guide" rel="noopener noreferrer"&gt;https://intelligencemax.ai/guide&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Transfer / measurement notes: &lt;a href="https://intelligencemax.ai/science" rel="noopener noreferrer"&gt;https://intelligencemax.ai/science&lt;/a&gt;&lt;br&gt;&lt;br&gt;
CSV / citation pack: &lt;a href="https://gist.github.com/connerlambden/f0ba06e1c98379250b1015f3ed14b867" rel="noopener noreferrer"&gt;https://gist.github.com/connerlambden/f0ba06e1c98379250b1015f3ed14b867&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Pocket litmus tests: &lt;a href="https://dev.to/connerlambden/five-litmus-tests-for-this-will-raise-your-intelligence-claims-7g"&gt;https://dev.to/connerlambden/five-litmus-tests-for-this-will-raise-your-intelligence-claims-7g&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built the live reasoning gym. Steal the grading scale without linking anything. Accuracy &amp;gt; vibes.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>marketing</category>
      <category>reviews</category>
      <category>science</category>
    </item>
    <item>
      <title>Five litmus tests for “this will raise your intelligence” claims</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 03:24:51 +0000</pubDate>
      <link>https://dev.to/connerlambden/five-litmus-tests-for-this-will-raise-your-intelligence-claims-7g</link>
      <guid>https://dev.to/connerlambden/five-litmus-tests-for-this-will-raise-your-intelligence-claims-7g</guid>
      <description>&lt;h1&gt;
  
  
  Five litmus tests for “this will raise your intelligence” claims
&lt;/h1&gt;

&lt;p&gt;A pocket BS detector for brain-training ads, LinkedIn gurus, and your own wishful thinking.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Which dial moved?
&lt;/h2&gt;

&lt;p&gt;Intelligence talk smuggles four dials into one word:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dial&lt;/th&gt;
&lt;th&gt;Rough meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;g / fluid ability&lt;/td&gt;
&lt;td&gt;Harder to move; overclaimed constantly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge &amp;amp; skill&lt;/td&gt;
&lt;td&gt;Moves with practice and education&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acute sharpness&lt;/td&gt;
&lt;td&gt;Sleep, illness, mood, substances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-horizon brain health&lt;/td&gt;
&lt;td&gt;Aging, disease risk, lifestyle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the claim does not say which dial, it is marketing soup.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What was the control?
&lt;/h2&gt;

&lt;p&gt;“People got better” is almost worthless. Better than last week of the same game is practice. Better than an &lt;strong&gt;active control&lt;/strong&gt; that also gets attention, novelty, and expectation is interesting. Lumosity-style lawsuits exist because companies sold soup as medicine.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Near or far?
&lt;/h2&gt;

&lt;p&gt;Near transfer: you got better at &lt;em&gt;this&lt;/em&gt; and close cousins.&lt;br&gt;&lt;br&gt;
Far transfer: the effect jumped to something distant (school grades, matrix reasoning, life outcomes).&lt;/p&gt;

&lt;p&gt;Far transfer is scarce. Second-order metas on cognitive training keep finding near yes, far ≈ no once bias is handled. That is not cynicism. It is how human learning usually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Who was the sample?
&lt;/h2&gt;

&lt;p&gt;A processing-speed protocol that helps older adults does not automatically mint IQ points for a 24-year-old optimization bro. Deficient populations respond differently than well-nourished ones. Age and baseline matter more than branding.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Is the score flattering the seller?
&lt;/h2&gt;

&lt;p&gt;If the outcome is “our app score,” the app got better at measuring app use. Prefer outcomes that hurt to fake: standardized batteries against active controls, academic scores, carefully logged real-world performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I built instead of another vanity mini-game
&lt;/h2&gt;

&lt;p&gt;IntelligenceMax is a live reasoning gym: frontier models write fresh distinction-style items at your edge, and scoring is transparent / IRT-style. That is deliberate practice under honest difficulty, not a clinical IQ battery and not a promise that general intelligence permanently rose.&lt;/p&gt;

&lt;p&gt;The part that should travel even if you never open the app: an evidence-ranked public map of the claim zoo.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guide: &lt;a href="https://intelligencemax.ai/guide" rel="noopener noreferrer"&gt;https://intelligencemax.ai/guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Science / transfer notes: &lt;a href="https://intelligencemax.ai/science" rel="noopener noreferrer"&gt;https://intelligencemax.ai/science&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Companion matrix files: &lt;a href="https://gist.github.com/connerlambden/f0ba06e1c98379250b1015f3ed14b867" rel="noopener noreferrer"&gt;https://gist.github.com/connerlambden/f0ba06e1c98379250b1015f3ed14b867&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclosure: I built it. Skeptical writers are welcome to steal the litmus tests without linking anything. Accuracy &amp;gt; vibes.&lt;/p&gt;

&lt;h2&gt;
  
  
  One sentence takeaway
&lt;/h2&gt;

&lt;p&gt;Want to get “smarter”? Fix sleep and constraints, learn hard things that matter, practice reasoning where you are wrong, and treat “raises IQ” like a claim that owes you a study design.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>I graded my own ML option forecasts. Here's the Brier score.</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Mon, 06 Jul 2026 17:47:14 +0000</pubDate>
      <link>https://dev.to/connerlambden/i-graded-my-own-ml-option-forecasts-heres-the-brier-score-1ake</link>
      <guid>https://dev.to/connerlambden/i-graded-my-own-ml-option-forecasts-heres-the-brier-score-1ake</guid>
      <description>&lt;p&gt;In May I published &lt;a href="https://dev.to/connerlambden/how-to-brier-grade-your-own-ml-option-pricing-forecasts-in-40-lines-of-python-2gb2"&gt;a 40-line recipe&lt;/a&gt; that logs Helium's &lt;code&gt;prob_itm&lt;/code&gt; on listed options and grades them at expiration with the Brier loss:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;brier_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prob_itm&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;realized_itm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also committed publicly to grading two June 2026 AAPL contracts when they expired. &lt;strong&gt;June 26 has passed.&lt;/strong&gt; This is the grade.&lt;/p&gt;

&lt;h2&gt;
  
  
  The frozen forecasts (2026-05-26 API)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contract&lt;/th&gt;
&lt;th&gt;Helium prob_itm&lt;/th&gt;
&lt;th&gt;Market-implied&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AAPL $310C 2026-06-26&lt;/td&gt;
&lt;td&gt;0.42&lt;/td&gt;
&lt;td&gt;~0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AAPL $295P 2026-06-26&lt;/td&gt;
&lt;td&gt;0.23&lt;/td&gt;
&lt;td&gt;~0.24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Resolution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AAPL NASDAQ close on 2026-06-26: $283.78&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$310 call → OTM → realized ITM = &lt;strong&gt;0&lt;/strong&gt; → Brier = &lt;strong&gt;(0.42 − 0)² = 0.1764&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;$295 put → ITM → realized ITM = &lt;strong&gt;1&lt;/strong&gt; → Brier = &lt;strong&gt;(0.23 − 1)² = 0.5929&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mean Brier loss: 0.3846&lt;/strong&gt; (n=2)&lt;/p&gt;

&lt;p&gt;Lower is better. Perfect = 0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest read
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Call:&lt;/strong&gt; Helium's 0.42 was closer to the realized 0 than the market's ~0.50, but any non-zero probability on an OTM finish still costs Brier points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put:&lt;/strong&gt; Probabilities looked aligned pre-expiry, but the put finished ITM after a sharp drawdown. Both market and model underestimated tail risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;n=2&lt;/strong&gt; is transparency, not proof. More expiries on the &lt;a href="https://connerlambden.github.io/helium-news-explorer/calibration.html" rel="noopener noreferrer"&gt;honesty board&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reproduce
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/connerlambden/helium-mcp-cookbook
&lt;span class="nb"&gt;cd &lt;/span&gt;helium-mcp-cookbook
python calibration/grade_june_expiry.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frozen inputs: &lt;a href="https://github.com/connerlambden/helium-mcp-cookbook/blob/main/calibration/june_2026_forecasts.json" rel="noopener noreferrer"&gt;&lt;code&gt;calibration/june_2026_forecasts.json&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://connerlambden.github.io/helium-news-explorer/calibration.html" rel="noopener noreferrer"&gt;Interactive honesty board&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://heliumtrades.com/mcp-page/" rel="noopener noreferrer"&gt;Helium MCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/connerlambden/helium-mcp-cookbook/blob/main/recipes/02_options_calibration_tracker.py" rel="noopener noreferrer"&gt;Recipe 02 — log your own forecasts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>statistics</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>A 260-line vanilla-JS dashboard for ML stock forecasts (one CORS-open MCP endpoint, zero dependencies)</title>
      <dc:creator>connerlambden</dc:creator>
      <pubDate>Wed, 27 May 2026 16:07:32 +0000</pubDate>
      <link>https://dev.to/connerlambden/a-260-line-vanilla-js-dashboard-for-ml-stock-forecasts-one-cors-open-mcp-endpoint-zero-2mk6</link>
      <guid>https://dev.to/connerlambden/a-260-line-vanilla-js-dashboard-for-ml-stock-forecasts-one-cors-open-mcp-endpoint-zero-2mk6</guid>
      <description>&lt;p&gt;I'm a fan of "smallest thing that could possibly work" demos for APIs. If a tool needs Node + Webpack + a build step + 800 KB of &lt;code&gt;node_modules&lt;/code&gt; before I can see what it does, I quietly close the tab.&lt;/p&gt;

&lt;p&gt;So when I wanted to make a visual surface for &lt;a href="https://github.com/connerlambden/helium-mcp" rel="noopener noreferrer"&gt;Helium MCP's&lt;/a&gt; top-10 volatility candidate ticker stream, I went the other way: a single HTML file, one fetch call, no dependencies, no build, ~260 lines of JS. It is live at:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://connerlambden.github.io/helium-news-explorer/tickers.html" rel="noopener noreferrer"&gt;connerlambden.github.io/helium-news-explorer/tickers.html&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is the architecture and the small handful of choices that made it work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The endpoint
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://heliumtrades.com/mcp_top_strategies/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns a JSON object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expected_vol_30d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_volatility"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tickers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"long_volatility"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tickers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each ticker entry packs everything I need into one object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NVDA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NVIDIA Corporation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"latest_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;138.45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price_forecast_days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price_forecast_percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price_forecast_lower_bound_percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-12.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price_forecast_upper_bound_percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;29.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bullish_case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;p&amp;gt;Blackwell ramp ahead of plan; ...&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bearish_case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;p&amp;gt;Hyperscaler capex digestion risk; ...&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"analysis_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-27"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"page_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's huge: the central forecast number AND the bull/bear narrative AND the uncertainty bounds come from the same model, so the chart you draw and the words you display agree on the same view of the world. No reconciliation step. No data joining. Just &lt;code&gt;fetch&lt;/code&gt;, then render.&lt;/p&gt;

&lt;p&gt;Crucially, Helium's MCP REST endpoints set &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;, so the page can fetch directly from the browser — no proxy, no server, no CORS tax.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole render path
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;…&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://heliumtrades.com/mcp_top_strategies/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div class="columns"&amp;gt;
      &amp;lt;div class="column short"&amp;gt;
        &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_volatility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;cardHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;short&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="column long"&amp;gt;
        &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;long_volatility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;cardHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;long&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two columns. Five cards each. Template literals as a render engine. That's it. No virtual DOM, no reactivity, no router. The page is static the moment the fetch resolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncertainty cone in 18 lines of SVG
&lt;/h2&gt;

&lt;p&gt;The interesting visual is the per-card &lt;em&gt;uncertainty cone&lt;/em&gt; — a small wedge that gets wider with time, shaded green or red based on the forecast direction, with a line through the center showing the central forecast and a label at the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderCone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forecastPct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lowerPct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperPct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;260&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;H&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PAD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxAbs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerPct&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upperPct&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                          &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forecastPct&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yZero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PAD&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;H&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;PAD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pctToY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;yZero&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;maxAbs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;H&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;PAD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;xEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;W&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;PAD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pctToY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upperPct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yDn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pctToY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerPct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yFc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pctToY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forecastPct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stroke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;forecastPct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#6bff9c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
               &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;forecastPct&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ff6b8a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
               &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8a93b8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fill&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,0.18)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgba(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;svg viewBox="0 0 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;W&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;H&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" preserveAspectRatio="none"&amp;gt;
    &amp;lt;polygon points="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;xStart&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yZero&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;xEnd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yUp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;xEnd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yDn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
             fill="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" stroke="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"/&amp;gt;
    &amp;lt;line x1="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;xStart&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" y1="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yZero&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" x2="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;xEnd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" y2="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yFc&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
          stroke="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" stroke-width="1.6"/&amp;gt;
    &amp;lt;circle cx="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;xEnd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" cy="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yFc&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" r="3" fill="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"/&amp;gt;
  &amp;lt;/svg&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cone is just a polygon with three points: &lt;code&gt;(start, 0)&lt;/code&gt;, &lt;code&gt;(end, upper)&lt;/code&gt;, &lt;code&gt;(end, lower)&lt;/code&gt;. The forecast line is a single segment from &lt;code&gt;(start, 0)&lt;/code&gt; to &lt;code&gt;(end, forecast)&lt;/code&gt;. The whole shape collapses to a point at the left edge ("today") and fans out on the right ("today + 37 days").&lt;/p&gt;

&lt;p&gt;I went back and forth on whether to plot the cone in absolute price terms vs. percent terms. Percent won: it makes a $69 KO card and a $1842 MSTR card visually comparable, since both cones are scaled to their own max-abs bound. You see the &lt;em&gt;shape&lt;/em&gt; of uncertainty, not the units.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why no chart library?
&lt;/h2&gt;

&lt;p&gt;Because for 10 small charts with one polygon and one line each, a chart library is more code than the chart itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;D3: 270 KB. Sets up &lt;code&gt;d3.scaleLinear()&lt;/code&gt;, &lt;code&gt;d3.area()&lt;/code&gt;, axes you don't need.&lt;/li&gt;
&lt;li&gt;Chart.js: 200 KB. Needs a canvas, animation loop, plugin system.&lt;/li&gt;
&lt;li&gt;ECharts: 1 MB+.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hand-rolled SVG: 18 lines. The browser already has a SVG renderer. The math is one division per coordinate. The styling lives in the same CSS file as the rest of the page. Less is more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bull/bear narratives are HTML
&lt;/h2&gt;

&lt;p&gt;Helium returns the cases as &lt;code&gt;&amp;lt;p&amp;gt;...&amp;lt;/p&amp;gt;&lt;/code&gt; strings. Easiest possible strip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stripHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then truncate to ~280 chars with a word-boundary check so you don't cut mid-word:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastSp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastSp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastSp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;…&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The card links to the full Helium analysis page for the long-form version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure handling
&lt;/h2&gt;

&lt;p&gt;The one real failure mode is a per-IP daily quota on the free tier (Helium returns HTTP 402 with no CORS headers, which the browser surfaces as a generic &lt;code&gt;TypeError: Failed to fetch&lt;/code&gt;). I handle it the boring honest way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`HTTP &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ... render ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Could not load forecasts: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.
    The Helium REST API enforces a per-IP daily quota for unauthenticated calls.
    Reload tomorrow, or browse the
    &amp;lt;a href="https://github.com/connerlambden/helium-mcp-cookbook"&amp;gt;cookbook&amp;lt;/a&amp;gt;.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A casual visitor will not hit this — each page load is one GET, and the free quota is ~50/day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Companion explorer
&lt;/h2&gt;

&lt;p&gt;The same repo also hosts a &lt;strong&gt;News Bias Explorer&lt;/strong&gt; at the root, built on the same pattern: one fetch to &lt;code&gt;mcp_all_source_biases/&lt;/code&gt;, vanilla-JS render of 216 sources × 37 bias dimensions, ranked bars and scatter plots with a live Pearson correlation. Same architecture, different data surface. &lt;a href="https://connerlambden.github.io/helium-news-explorer/" rel="noopener noreferrer"&gt;connerlambden.github.io/helium-news-explorer&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for the MCP ecosystem
&lt;/h2&gt;

&lt;p&gt;Most MCP server demos are dense Python notebooks or CLI tools. They demonstrate the protocol but not the &lt;em&gt;data&lt;/em&gt;. A casual visitor — a journalist evaluating bias-scoring tools, a retail trader curious about an options model, a researcher looking for a teaching corpus — won't install Python to find out.&lt;/p&gt;

&lt;p&gt;A 21 KB HTML file on GitHub Pages, with no signup and no key, is the lowest-friction path from "I just heard about this server" to "oh, here's what it can do."&lt;/p&gt;

&lt;p&gt;If you're building an MCP server, consider shipping a small zero-dep web surface next to your CLI examples. The marginal cost is one HTML file. The marginal benefit is every browser becoming a demo client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live dashboard&lt;/strong&gt;: &lt;a href="https://connerlambden.github.io/helium-news-explorer/tickers.html" rel="noopener noreferrer"&gt;connerlambden.github.io/helium-news-explorer/tickers.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live news bias explorer&lt;/strong&gt;: &lt;a href="https://connerlambden.github.io/helium-news-explorer/" rel="noopener noreferrer"&gt;connerlambden.github.io/helium-news-explorer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://github.com/connerlambden/helium-news-explorer" rel="noopener noreferrer"&gt;github.com/connerlambden/helium-news-explorer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python recipes for the same API&lt;/strong&gt;: &lt;a href="https://github.com/connerlambden/helium-mcp-cookbook" rel="noopener noreferrer"&gt;github.com/connerlambden/helium-mcp-cookbook&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helium MCP server&lt;/strong&gt;: &lt;a href="https://github.com/connerlambden/helium-mcp" rel="noopener noreferrer"&gt;github.com/connerlambden/helium-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The forecasts and bias scores are educational. Not investment advice. Track calibration; the model is wrong all the time.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>mcp</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
