<?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: Lucian (LKB)</title>
    <description>The latest articles on DEV Community by Lucian (LKB) (@lucian_lkb_1f009d).</description>
    <link>https://dev.to/lucian_lkb_1f009d</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%2F4026097%2Feb9247a0-cd26-41b9-9613-ddcb120a9d66.jpg</url>
      <title>DEV Community: Lucian (LKB)</title>
      <link>https://dev.to/lucian_lkb_1f009d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucian_lkb_1f009d"/>
    <language>en</language>
    <item>
      <title>I built an open, reproducible typing-speed test — and it won't show a number until it earns one</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Sat, 01 Aug 2026 14:48:49 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/i-built-an-open-reproducible-typing-speed-test-and-it-wont-show-a-number-until-it-earns-one-16md</link>
      <guid>https://dev.to/lucian_lkb_1f009d/i-built-an-open-reproducible-typing-speed-test-and-it-wont-show-a-number-until-it-earns-one-16md</guid>
      <description>&lt;p&gt;Search "average typing speed" and you'll get confident numbers — 40 WPM, 65 WPM, "professionals hit 75" — almost always with no passage, no method, and no dataset behind them. I wanted one I could actually defend, so I built a live typing test that collects an open, anonymized dataset and, crucially, &lt;strong&gt;refuses to publish an average until it has enough data to mean anything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It runs on Cloudflare Workers + D1. It's live, the dataset is downloadable, and every headline number is reproducible from a script. Here's how it's built and the decisions that keep it honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why WPM is hard to measure fairly
&lt;/h2&gt;

&lt;p&gt;Three problems sink most "typing speed" numbers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Comparability.&lt;/strong&gt; If everyone types different text, WPM isn't comparable — a familiar sentence types faster than a technical one. Fix: &lt;strong&gt;one fixed, standardized passage&lt;/strong&gt; for everyone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gross vs. net.&lt;/strong&gt; Counting every keystroke rewards fast-and-sloppy. Fix: &lt;strong&gt;net WPM&lt;/strong&gt; — only characters that match the passage &lt;em&gt;at the same position&lt;/em&gt; count, so mistakes lower your score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheating.&lt;/strong&gt; A paste "types" 300 WPM. Fix: block paste, and reject anything outside a plausible human range server-side.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The scoring is deliberately boring and standard:&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="c1"&gt;// a character counts only if it matches the passage at that position&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;correct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&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;typed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;passage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;correct&lt;/span&gt;&lt;span class="o"&gt;++&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;netWpm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;correct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&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;elapsedMs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 5 chars = 1 "word"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accuracy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;correct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;typed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// as a percentage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Keeping submissions honest (without accounts)
&lt;/h2&gt;

&lt;p&gt;No login, but a few cheap gates keep the dataset clean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A short-lived &lt;strong&gt;HMAC token&lt;/strong&gt; fetched right before submit — blocks tokenless &lt;code&gt;curl&lt;/code&gt; spam.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;1–250 WPM&lt;/strong&gt; plausibility bound — a paste or bot lands well above 250 and is dropped.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;per-visitor daily cap&lt;/strong&gt;, so one source can't skew the set.&lt;/li&gt;
&lt;li&gt;Privacy: the server never stores your IP. It keeps a &lt;strong&gt;salted SHA-256 hash&lt;/strong&gt; purely for rate-limiting, and the public export contains only &lt;code&gt;timestamp, wpm, accuracy&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The part I care about most: the honesty gate
&lt;/h2&gt;

&lt;p&gt;With a few dozen samples, one fat-fingered run swings the average wildly. So the page &lt;strong&gt;hides the headline until it has 1,000 results&lt;/strong&gt;, and reports the &lt;strong&gt;median&lt;/strong&gt; (robust to outliers) rather than the mean:&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;renderHeadline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&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;`Collecting data — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&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="s2"&gt; tests so far.`&lt;/span&gt;&lt;span class="p"&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;`The median typist reaches &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;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;median_wpm&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; net WPM `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
         &lt;span class="s2"&gt;`at &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;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;median_accuracy&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;% accuracy — over &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; tests.`&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;Right now it genuinely says &lt;em&gt;"collecting data — 0 tests."&lt;/em&gt; That's the point: I'd rather show an honest zero than a made-up average. Typing speed is a trained skill, so the honest description isn't a single number anyway — it's a &lt;strong&gt;distribution&lt;/strong&gt;, which the page draws as a live histogram once results come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's reproducible
&lt;/h2&gt;

&lt;p&gt;The stats aren't a black box. There's a public CSV export and a small Node harness that recomputes every published figure — median WPM, median accuracy, trimmed mean, percentiles, histogram — using a &lt;strong&gt;verbatim copy of the site's own stats code&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node typing-speed-study.mjs
&lt;span class="c"&gt;# fetches https://lkforge.com/api/typing/export and recomputes the numbers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the claim isn't "trust my dashboard," it's "here's the raw data and the exact math — run it yourself."&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it (and feed the dataset)
&lt;/h2&gt;

&lt;p&gt;The whole thing only becomes interesting with data, and it's at zero. If you want to add a point and watch the distribution build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Take the test:&lt;/strong&gt; &lt;a href="https://lkforge.com/tools/typingtrack/typing-speed-study/" rel="noopener noreferrer"&gt;https://lkforge.com/tools/typingtrack/typing-speed-study/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce the numbers:&lt;/strong&gt; &lt;a href="https://gist.github.com/lucian-devops/063798164ec86d076e977d003eb87c30" rel="noopener noreferrer"&gt;https://gist.github.com/lucian-devops/063798164ec86d076e977d003eb87c30&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw dataset (CSV, CC BY 4.0):&lt;/strong&gt; &lt;a href="https://lkforge.com/api/typing/export" rel="noopener noreferrer"&gt;https://lkforge.com/api/typing/export&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll write a follow-up with the actual distribution once there's enough data to report one honestly. Curious what people think of the honesty-gate approach — worth the "boring zero," or would you just show the running average with a caveat?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>cloudflare</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Six Games, Three Classic Algorithms: Shipping Real Game AI in Vanilla JS</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Thu, 30 Jul 2026 17:24:55 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/six-games-three-classic-algorithms-shipping-real-game-ai-in-vanilla-js-4009</link>
      <guid>https://dev.to/lucian_lkb_1f009d/six-games-three-classic-algorithms-shipping-real-game-ai-in-vanilla-js-4009</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Syndicated from my blog. Canonical: &lt;a href="https://lkforge.com/blog/game-ai-three-algorithms/" rel="noopener noreferrer"&gt;https://lkforge.com/blog/game-ai-three-algorithms/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I built six small browser games — 2048, tic-tac-toe, sudoku, and a few others — and gave each a real AI opponent or solver. No libraries, no WASM, no server calls. Three textbook search algorithms cover all of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimax with alpha-beta pruning&lt;/strong&gt; — perfect play for adversarial, fully-observable games (tic-tac-toe).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expectimax&lt;/strong&gt; — the same idea, but averaging over &lt;em&gt;chance&lt;/em&gt; nodes instead of assuming a rational opponent. This is what drives the 2048 solver, because tile spawns are random.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BFS pathfinding&lt;/strong&gt; — for the games that are really shortest-path problems in disguise.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 2048 solver, concretely
&lt;/h2&gt;

&lt;p&gt;2048 isn't adversarial — there's no opponent, just a 90/10 random 2-or-4 spawn after each move. So minimax is the wrong tool; you want &lt;strong&gt;expectimax&lt;/strong&gt;, which alternates &lt;em&gt;max&lt;/em&gt; nodes (your move) with &lt;em&gt;chance&lt;/em&gt; nodes (the random spawn), weighting each outcome by probability.&lt;/p&gt;

&lt;p&gt;The heuristic is what actually matters. Mine rewards three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A corner-snake gradient&lt;/strong&gt; — a weight matrix that pins the largest tile in one corner and wants values to descend in a boustrophedon "snake" from there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empty cells&lt;/strong&gt; — heavily weighted, because running out of space is how you lose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smoothness&lt;/strong&gt; — adjacent tiles close in log2 value, so merges stay available.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Search depth adapts to how full the board is (deeper when there's more empty space to reason about), and chance nodes sample a bounded number of empty cells so branching stays tractable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part: I measured it instead of claiming it
&lt;/h2&gt;

&lt;p&gt;It's easy to write "unbeatable AI" in a README. I ran a headless self-play harness instead — thousands of games, logged outcomes — and published the real numbers, including the unflattering ones (a depth-capped tic-tac-toe search &lt;em&gt;can&lt;/em&gt; still drop a game; the 2048 solver reaches the 2048 tile ~70% of the time, 4096 ~30%, and never 8192 in my runs). The full benchmark write-up and the reproducible harness are linked from the canonical post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why vanilla JS
&lt;/h2&gt;

&lt;p&gt;Zero dependencies means the whole thing ships as a static file, runs entirely in the visitor's browser, loads instantly, and never phones home. For small games that's not a constraint — it's the right call.&lt;/p&gt;

&lt;p&gt;Full post, charts, and the "run it yourself" harness: &lt;a href="https://lkforge.com/blog/game-ai-three-algorithms/" rel="noopener noreferrer"&gt;https://lkforge.com/blog/game-ai-three-algorithms/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also play the 2048 solver (Hint names the best move; Auto-Play runs the whole strategy live): &lt;a href="https://lkforge.com/games/2048/" rel="noopener noreferrer"&gt;https://lkforge.com/games/2048/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>gamedev</category>
      <category>algorithms</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How Much Does It Cost to Charge an Electric Car? I Ran the Numbers for 10 EVs</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Thu, 30 Jul 2026 04:22:09 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/how-much-does-it-cost-to-charge-an-electric-car-i-ran-the-numbers-for-10-evs-3kan</link>
      <guid>https://dev.to/lucian_lkb_1f009d/how-much-does-it-cost-to-charge-an-electric-car-i-ran-the-numbers-for-10-evs-3kan</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="https://lkforge.com/blog/cost-to-charge-an-electric-car/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;, where the charts are interactive and you can plug in your own car and rate with the &lt;a href="https://lkforge.com/tools/calculators/ev-charging-cost/" rel="noopener noreferrer"&gt;EV Charging Cost Calculator&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The honest answer to "how much does it cost to charge an electric car" is &lt;em&gt;it depends&lt;/em&gt; — on your battery, your car's efficiency, and above all where you plug in. So I ran one transparent model across 10 popular EVs at three places you charge: at home, on a public Level 2 station, and on a DC fast charger. Here is what a charge actually costs, per full charge and per mile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The savings are in the garage.&lt;/strong&gt; Charging at home is where an EV is cheap to run — about &lt;strong&gt;4.6¢ a mile&lt;/strong&gt; for an efficient car. But at typical DC fast-charging prices the same car costs &lt;strong&gt;13.0¢ a mile&lt;/strong&gt;, and a big truck can top 27¢ — at or above what a 30-mpg gas car costs per mile. The road, not the car, decides the savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  At home, a mile costs a few cents
&lt;/h2&gt;

&lt;p&gt;Charged at home at $0.17 per kWh, every car here comes in under 10¢ a mile, and the efficient sedans sit near 4.6¢. The number tracks efficiency almost perfectly — the more miles a car gets per kWh, the less each mile costs. Heavy trucks use roughly twice the energy per mile of a Model 3, so they cost roughly twice as much to drive on the same electricity.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;EV&lt;/th&gt;
&lt;th&gt;Home cost per mile&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tesla Model 3 (RWD)&lt;/td&gt;
&lt;td&gt;4.6¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chevrolet Bolt EV&lt;/td&gt;
&lt;td&gt;4.8¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tesla Model Y&lt;/td&gt;
&lt;td&gt;5.1¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nissan Leaf&lt;/td&gt;
&lt;td&gt;5.4¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyundai Ioniq 5&lt;/td&gt;
&lt;td&gt;5.6¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kia EV6&lt;/td&gt;
&lt;td&gt;5.6¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ford Mustang Mach-E&lt;/td&gt;
&lt;td&gt;6.3¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volkswagen ID.4&lt;/td&gt;
&lt;td&gt;6.3¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rivian R1T&lt;/td&gt;
&lt;td&gt;8.2¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ford F-150 Lightning&lt;/td&gt;
&lt;td&gt;9.4¢&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Where you charge matters more than what you drive
&lt;/h2&gt;

&lt;p&gt;The rate per kWh is the biggest lever on cost, and it swings hard by location. Public Level 2 typically runs around $0.30 per kWh and DC fast charging around $0.48 — roughly &lt;strong&gt;2.8× the home rate&lt;/strong&gt;. That multiplier lands on every mile:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;EV&lt;/th&gt;
&lt;th&gt;Home ($0.17)&lt;/th&gt;
&lt;th&gt;Public L2 ($0.30)&lt;/th&gt;
&lt;th&gt;DC fast ($0.48)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tesla Model 3 (RWD)&lt;/td&gt;
&lt;td&gt;4.6¢&lt;/td&gt;
&lt;td&gt;8.1¢&lt;/td&gt;
&lt;td&gt;13.0¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ford Mustang Mach-E&lt;/td&gt;
&lt;td&gt;6.3¢&lt;/td&gt;
&lt;td&gt;11.1¢&lt;/td&gt;
&lt;td&gt;17.8¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ford F-150 Lightning&lt;/td&gt;
&lt;td&gt;9.4¢&lt;/td&gt;
&lt;td&gt;16.7¢&lt;/td&gt;
&lt;td&gt;26.7¢&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For comparison, a 30-mpg gas car at $3.50 a gallon costs about &lt;strong&gt;11.7¢ a mile&lt;/strong&gt;. Home charging beats it comfortably for every EV here; DC fast charging meets or exceeds it for most. That is the practical takeaway: an EV is cheapest when you charge slowly overnight and treat fast charging as the convenience it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full 10-car table
&lt;/h2&gt;

&lt;p&gt;Full charge is a 0–100% home charge at $0.17/kWh; the per-mile columns use $0.17 (home), $0.30 (Level 2) and $0.48 (DC fast) per kWh, all at 90% charging efficiency.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;EV&lt;/th&gt;
&lt;th&gt;Battery&lt;/th&gt;
&lt;th&gt;mi/kWh&lt;/th&gt;
&lt;th&gt;Full charge&lt;/th&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;th&gt;L2&lt;/th&gt;
&lt;th&gt;DC fast&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tesla Model 3 (RWD)&lt;/td&gt;
&lt;td&gt;57.5 kWh&lt;/td&gt;
&lt;td&gt;4.1&lt;/td&gt;
&lt;td&gt;$10.86&lt;/td&gt;
&lt;td&gt;4.6¢&lt;/td&gt;
&lt;td&gt;8.1¢&lt;/td&gt;
&lt;td&gt;13.0¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chevrolet Bolt EV&lt;/td&gt;
&lt;td&gt;65 kWh&lt;/td&gt;
&lt;td&gt;3.9&lt;/td&gt;
&lt;td&gt;$12.28&lt;/td&gt;
&lt;td&gt;4.8¢&lt;/td&gt;
&lt;td&gt;8.5¢&lt;/td&gt;
&lt;td&gt;13.7¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tesla Model Y&lt;/td&gt;
&lt;td&gt;75 kWh&lt;/td&gt;
&lt;td&gt;3.7&lt;/td&gt;
&lt;td&gt;$14.17&lt;/td&gt;
&lt;td&gt;5.1¢&lt;/td&gt;
&lt;td&gt;9.0¢&lt;/td&gt;
&lt;td&gt;14.4¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nissan Leaf&lt;/td&gt;
&lt;td&gt;60 kWh&lt;/td&gt;
&lt;td&gt;3.5&lt;/td&gt;
&lt;td&gt;$11.33&lt;/td&gt;
&lt;td&gt;5.4¢&lt;/td&gt;
&lt;td&gt;9.5¢&lt;/td&gt;
&lt;td&gt;15.2¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyundai Ioniq 5&lt;/td&gt;
&lt;td&gt;77 kWh&lt;/td&gt;
&lt;td&gt;3.4&lt;/td&gt;
&lt;td&gt;$14.54&lt;/td&gt;
&lt;td&gt;5.6¢&lt;/td&gt;
&lt;td&gt;9.8¢&lt;/td&gt;
&lt;td&gt;15.7¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kia EV6&lt;/td&gt;
&lt;td&gt;77 kWh&lt;/td&gt;
&lt;td&gt;3.4&lt;/td&gt;
&lt;td&gt;$14.54&lt;/td&gt;
&lt;td&gt;5.6¢&lt;/td&gt;
&lt;td&gt;9.8¢&lt;/td&gt;
&lt;td&gt;15.7¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ford Mustang Mach-E&lt;/td&gt;
&lt;td&gt;88 kWh&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;td&gt;$16.62&lt;/td&gt;
&lt;td&gt;6.3¢&lt;/td&gt;
&lt;td&gt;11.1¢&lt;/td&gt;
&lt;td&gt;17.8¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volkswagen ID.4&lt;/td&gt;
&lt;td&gt;82 kWh&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;td&gt;$15.49&lt;/td&gt;
&lt;td&gt;6.3¢&lt;/td&gt;
&lt;td&gt;11.1¢&lt;/td&gt;
&lt;td&gt;17.8¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rivian R1T&lt;/td&gt;
&lt;td&gt;135 kWh&lt;/td&gt;
&lt;td&gt;2.3&lt;/td&gt;
&lt;td&gt;$25.50&lt;/td&gt;
&lt;td&gt;8.2¢&lt;/td&gt;
&lt;td&gt;14.5¢&lt;/td&gt;
&lt;td&gt;23.2¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ford F-150 Lightning&lt;/td&gt;
&lt;td&gt;131 kWh&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;$24.74&lt;/td&gt;
&lt;td&gt;9.4¢&lt;/td&gt;
&lt;td&gt;16.7¢&lt;/td&gt;
&lt;td&gt;26.7¢&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to work out your own cost
&lt;/h2&gt;

&lt;p&gt;Every number here comes from two lines of arithmetic. To top up from one state of charge to another, the energy that reaches the battery is the battery size times the percentage added; you pay for a bit more than that because of charging losses. Then multiply by your rate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Charge cost&lt;/strong&gt; = (battery kWh × percent added) ÷ efficiency × rate per kWh&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost per mile&lt;/strong&gt; = (rate per kWh ÷ efficiency) ÷ miles per kWh&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assumed rates:&lt;/strong&gt; home $0.17, public Level 2 $0.30, DC fast $0.48 per kWh&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assumed efficiency:&lt;/strong&gt; 90% (wall to battery); battery and mi/kWh per the table above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are illustrative assumptions, not measured field data — charging losses, network pricing and driving conditions vary. Plug in the rate from your own bill or charger for an exact figure with the &lt;a href="https://lkforge.com/tools/calculators/ev-charging-cost/" rel="noopener noreferrer"&gt;EV Charging Cost Calculator&lt;/a&gt;, or estimate your whole monthly bill with the &lt;a href="https://lkforge.com/tools/calculators/electricity/" rel="noopener noreferrer"&gt;Electricity Bill Calculator&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ev</category>
      <category>electricvehicles</category>
      <category>data</category>
      <category>sustainability</category>
    </item>
    <item>
      <title>How Random Is a Random Word Generator?</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Sat, 25 Jul 2026 19:31:55 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/how-random-is-a-random-word-generator-290p</link>
      <guid>https://dev.to/lucian_lkb_1f009d/how-random-is-a-random-word-generator-290p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This was originally published on &lt;a href="https://lkforge.com/blog/random-word-generator-randomness/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;, where the charts are interactive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Random" is a claim, not a given — a generator that quietly favors a handful of words isn't random, it's just unpredictable-looking. We ran 20,000 seeded draws against our Word Generator's default word pool and tested the results with a chi-square goodness-of-fit test to find out whether every word really does have an equal shot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Across 20,000 draws from the 365-word default pool, the generator samples uniformly: chi-square = 320.572 on 364 degrees of freedom, giving p = 0.9509&lt;/strong&gt; — far above the 0.05 threshold for suspecting bias. No word in the pool is favored or starved over the other 364. Statistically, it's fair.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Draws sampled&lt;/td&gt;
&lt;td&gt;20,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chi-square p-value&lt;/td&gt;
&lt;td&gt;0.9509&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Words in the default pool&lt;/td&gt;
&lt;td&gt;365&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expected draws per word&lt;/td&gt;
&lt;td&gt;54.8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Is the sampling fair?
&lt;/h2&gt;

&lt;p&gt;A chi-square goodness-of-fit test compares observed draw counts against what a perfectly uniform sampler would produce. With 20,000 draws spread across a 365-word pool, each word should show up about 54.8 times on average if every word is equally likely — that's the expected count per cell, comfortably above the rule-of-thumb minimum of 5 needed for the test to be valid without merging cells. The null hypothesis is "every word is equally likely to be drawn."&lt;/p&gt;

&lt;p&gt;The observed chi-square statistic came out to 320.572 against 364 degrees of freedom (pool size minus one), which converts to a p-value of 0.9509. Because that p-value sits far above the 0.05 significance threshold, we fail to reject the null: the deviations we see between words are consistent with ordinary sampling noise, not favoritism. That's the whole "how random" verdict — it says nothing yet about what the generated words look like, which is a separate question below.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a generated word looks like
&lt;/h2&gt;

&lt;p&gt;This next chart is descriptive, not a randomness test: it compares how often each letter appeared across the 20,000 generated words against how often that letter appears in general English text. It answers "what does the output look like," while the chi-square test above answers "is the sampling fair." The two questions are independent — a generator can sample its pool with perfect uniformity while that pool's letter mix still differs from English at large, because the pool's word list, not the RNG, decides which letters show up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fptihohnegd56fn0srhwz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fptihohnegd56fn0srhwz.png" alt="Grouped bar chart comparing letter frequency across 20,000 generated words against general English text, one pair of bars per letter a through z. E is the most common letter in both series (13.18% generated vs 12.702% English); H shows the largest gap, at 2.5% generated versus 6.094% English." width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The widest gap belongs to H: it makes up just 2.5% of letters in the generated words but 6.094% of general English text, a 3.594-point difference. That's a fact about which 365 words happen to be in this pool — plenty of them are short and H-light — not a sign of anything skewed in how those words get picked. The chi-square test above already confirmed the picking itself is uniform.&lt;/p&gt;

&lt;h2&gt;
  
  
  How long are generated words?
&lt;/h2&gt;

&lt;p&gt;Same caveat as above: this is a description of the pool's word lengths, not a randomness measurement. Across the 20,000 draws, 6-letter words came up most often (7,190 times), closely followed by 5-letter words (6,791), with a sharp drop-off at the short end (178 three-letter words) and the long end (48 nine-letter words). That shape simply mirrors how many words of each length exist in the 365-word pool — draw uniformly from a pool with more 6-letter words than 3-letter words, and 6-letter results will naturally come up more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7wh0ym61s2bfvz560lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7wh0ym61s2bfvz560lc.png" alt="Bar chart of word-length distribution across 20,000 generated words, one bar per length from 3 to 9 letters. The tallest bar is 6 letters with 7,190 words, followed by 5 letters with 6,791; the shortest and longest bars, 3 and 9 letters, are the smallest at 178 and 48 words." width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this study does — and doesn't — cover
&lt;/h2&gt;

&lt;p&gt;This study characterizes one thing: the generator's default pool, the 365-word list you get with the Word type filter left on "Words." The Word Generator also ships 8 specialty category lists reachable from that same dropdown — Animals, Birds, Colors, Compound Words, Elements, Flowers, Prepositions, and Spanish Words — each drawing from its own separate word list. None of those 8 lists were sampled or tested here, and this study makes no claim about their sampling fairness or letter/length makeup. Every figure above describes the default pool only.&lt;/p&gt;

&lt;h2&gt;
  
  
  A seeded stand-in for Math.random()
&lt;/h2&gt;

&lt;p&gt;The live Word Generator draws with JavaScript's built-in &lt;code&gt;Math.random()&lt;/code&gt;, which isn't seedable — running it twice gives two different outcomes, so it can't be independently reproduced. For this study we substituted a seeded pseudorandom generator, mulberry32, initialized with seed 42, so anyone can re-run the exact same 20,000 draws and get the exact same chi-square result. That substitution measures the distribution the generator's sampling mechanism (a Fisher-Yates-style draw from the pool) produces, not the specific bit sequence of the browser's own random number source — the sampling logic under test is identical either way, only the source of randomness feeding it changes for reproducibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it yourself
&lt;/h2&gt;

&lt;p&gt;The harness is public and deterministic — one script that runs mulberry32 (seed 42) through 20,000 draws in 400 batches of 50 against the generator's shipped default 365-word pool, printing the chi-square statistic, p-value, letter-frequency table, and length distribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gist.github.com/lucian-devops/9dcd34ccb968835b70234dcda37b928f" rel="noopener noreferrer"&gt;Random word generator randomness harness (public gist)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every number on this page is printed by that one script. Run it yourself to check any figure above to the exact digit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The interactive charts are on &lt;a href="https://lkforge.com/blog/random-word-generator-randomness/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Try the tool: &lt;strong&gt;&lt;a href="https://lkforge.com/tools/wordgenerator/" rel="noopener noreferrer"&gt;Word Generator&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>statistics</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>WWF vs Scrabble Scoring — Where the Two Games Diverge</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Sat, 25 Jul 2026 19:23:36 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/wwf-vs-scrabble-scoring-where-the-two-games-diverge-4d5</link>
      <guid>https://dev.to/lucian_lkb_1f009d/wwf-vs-scrabble-scoring-where-the-two-games-diverge-4d5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This was originally published on &lt;a href="https://lkforge.com/blog/wwf-vs-scrabble-scoring/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;, where the charts are interactive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Words With Friends and Scrabble share a board concept but value letters differently. We scored all 172,835 words in the &lt;strong&gt;ENABLE open word list&lt;/strong&gt; — not the TWL or CSW/SOWPODS Scrabble dictionaries, just the open list — against both letter-value tables. &lt;strong&gt;agammaglobulinemic&lt;/strong&gt; scores 41 points in Words With Friends against 30 in Scrabble, an 11-point swing. Hunt the other direction and the biggest Scrabble edge you can find, on &lt;strong&gt;dysrhythmia&lt;/strong&gt;, is only −3.&lt;/p&gt;

&lt;p&gt;That ceiling isn't a coincidence. Only two letters — H and Y — are worth more in Scrabble than in WWF. Ten others (B, C, G, J, L, M, N, P, U, V) are worth more in WWF. So no word can lean on a Scrabble advantage the way it can lean on a WWF one. Across the full list, 8.02% of words (13,862) score identically either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ENABLE words scored&lt;/td&gt;
&lt;td&gt;172,835&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Score identically&lt;/td&gt;
&lt;td&gt;8.02% (13,862)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean absolute point gap&lt;/td&gt;
&lt;td&gt;2.528&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Widest WWF vs Scrabble swing&lt;/td&gt;
&lt;td&gt;+11 / −3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How far scores diverge
&lt;/h2&gt;

&lt;p&gt;Delta is a word's WWF score minus its Scrabble score. A delta of 0 means the two games agree; positive means WWF pays more; negative means Scrabble pays more. Across all 172,835 ENABLE words the mean absolute delta is 2.528 points, and the distribution is lopsided — it stretches out to +11 on the WWF side but stops cold at −3 on the Scrabble side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmt114ooiwteyli9fm88a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmt114ooiwteyli9fm88a.png" alt="Bar chart of WWF-minus-Scrabble point delta across all 172,835 ENABLE words, one bar per delta bucket from -3 to +11. The tallest bar is +2 with 41,873 words; the distribution is long on the positive side and short on the negative side, stopping at -3" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the gap is lopsided
&lt;/h2&gt;

&lt;p&gt;The asymmetry traces straight back to the two letter-value tables. Compare them letter by letter and B, C, G, J, L, M, N, P, U and V are all worth more in WWF than in standard Scrabble scoring — a long list of common consonants and vowels quietly worth extra points there. Going the other way, only two letters, H and Y, are worth more in Scrabble than in WWF. Every other letter is valued identically. That's why a word stuffed with B's, C's, and M's can pile up a double-digit WWF lead, while the best a word can do by leaning on H and Y is claw back three points.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn4dkogneyt8osl9db51s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn4dkogneyt8osl9db51s.png" alt="Grouped bar chart of all 26 letters showing Scrabble tile value next to Words With Friends tile value. Ten letters (B, C, G, J, L, M, N, P, U, V) are taller on the WWF side; only H and Y are taller on the Scrabble side, highlighted in red; the rest are tied" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That single structural fact explains the whole shape of the histogram above: a tall, wide right side (WWF-favoring deltas run from +1 all the way to +11) and a short, thin left side (Scrabble-favoring deltas can't go past −3 because H and Y are the only levers available, and most words only carry one or two of either).&lt;/p&gt;

&lt;h2&gt;
  
  
  The widest gaps
&lt;/h2&gt;

&lt;p&gt;The 15 widest swings in each direction are all long, uncommon words — the kind of vocabulary that shows up only when a rack forces an unusual play, but a clean illustration of how far the tables can pull apart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where WWF scores highest over Scrabble:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Word&lt;/th&gt;
&lt;th&gt;WWF&lt;/th&gt;
&lt;th&gt;Scrabble&lt;/th&gt;
&lt;th&gt;Δ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;agammaglobulinemic&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;+11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agammaglobulinemia&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agammaglobulinemias&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;circumambulating&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;counterinfluencing&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;immunoglobulin&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;immunoglobulins&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macroglobulinemic&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;noncommunicating&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uncommunicable&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unconvincingness&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unconvincingnesses&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;+10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bejumbling&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;+9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;buckminsterfullerene&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;+9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;buckminsterfullerenes&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;+9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Where Scrabble scores highest over WWF:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Word&lt;/th&gt;
&lt;th&gt;WWF&lt;/th&gt;
&lt;th&gt;Scrabble&lt;/th&gt;
&lt;th&gt;Δ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;dysrhythmia&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dysrhythmias&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasheesh&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hasheeshes&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hashhead&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hashheads&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hashish&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hashishes&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;heathery&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;heathy&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hexahydrate&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hexahydrates&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;heyday&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;heydays&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;heydey&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;−3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice the pattern in the Scrabble-favoring column: every entry carries at least one H or Y, and several stack more than one — &lt;strong&gt;dysrhythmia&lt;/strong&gt; repeats both letters (two H's, two Y's) and &lt;strong&gt;hexahydrate&lt;/strong&gt; repeats the H. That repetition isn't a coincidence given the letter-value gap above; it's the only route to a negative delta at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means at the board
&lt;/h2&gt;

&lt;p&gt;In practice, this means a word finder that only scores Scrabble-style is quietly under-counting nearly every real Words With Friends play. Racks heavy in B, C, G, J, L, M, N, P, U, or V are worth chasing harder in WWF than a Scrabble instinct would suggest, while H and Y give a small but real edge back to Scrabble players comparing the same word. It's also why the same rack can produce a different "best play" depending on which game you're actually in — the ranking, not just the total, can flip.&lt;/p&gt;

&lt;p&gt;That's the reason the &lt;a href="https://lkforge.com/tools/wordunscrambler/words-with-friends-cheat/" rel="noopener noreferrer"&gt;Words With Friends Cheat&lt;/a&gt; shows both a WWF badge and a Scrabble badge on every result — sorting by the wrong one costs real points, and the two numbers on this page show exactly how much.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Scrabble and Words With Friends references
&lt;/h2&gt;

&lt;p&gt;SCRABBLE and Words With Friends are referenced here only descriptively, as the source of their public letter-value tables — public reference points for comparison, not an official analysis by either trademark holder. Both are trademarks of their respective owners; this post is not affiliated with, endorsed by, or sponsored by either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it yourself
&lt;/h2&gt;

&lt;p&gt;The harness is public and deterministic — one script scores every word in the shipped ENABLE list (172,835 words) against both the Scrabble and WWF tile-value tables and records the delta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gist.github.com/lucian-devops/21fff2526147b5dcc88af06911c929b3" rel="noopener noreferrer"&gt;WWF vs Scrabble scoring harness (public gist)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every number on this page is printed by that one script. Run it yourself to check any figure above to the exact digit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The interactive charts are on &lt;a href="https://lkforge.com/blog/wwf-vs-scrabble-scoring/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordgames</category>
      <category>scrabble</category>
      <category>data</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How Long Is Golden Hour? We Measured It for 27 Cities</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Sat, 25 Jul 2026 19:20:13 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/how-long-is-golden-hour-we-measured-it-for-27-cities-8bd</link>
      <guid>https://dev.to/lucian_lkb_1f009d/how-long-is-golden-hour-we-measured-it-for-27-cities-8bd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This was originally published on &lt;a href="https://lkforge.com/blog/how-long-is-golden-hour/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;, where the charts are interactive and every city links to its live local time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Golden hour is almost never an hour. Photo apps and calendars round it to a flat 60 minutes, but that number isn't measured — it's a convenience. Using the NOAA solar-position algorithm, we computed the real duration — sun between +6° and the horizon, evening — for 27 cities across three dates in 2026. It's shorter than advertised almost everywhere, and it never reaches an hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shortest golden hour&lt;/td&gt;
&lt;td&gt;24 min — Singapore, equinox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Longest in the set&lt;/td&gt;
&lt;td&gt;57.6 min — Berlin, Dec solstice (still under an hour)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average at the March equinox (27 cities)&lt;/td&gt;
&lt;td&gt;30.2 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Longer at 52°N than at the equator (equinox)&lt;/td&gt;
&lt;td&gt;1.7×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 60-minute golden hour is a myth. At the March equinox not one of these 27 cities reaches 45 minutes — the average is about 30.2 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Golden hour gets longer as you leave the equator
&lt;/h2&gt;

&lt;p&gt;Sorted by absolute latitude, the equinox golden hour lengthens almost monotonically from the equator to the highest-latitude city in the set — Singapore at 24 minutes up to Berlin at 39.6 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ib68rptzap58oe55b0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ib68rptzap58oe55b0h.png" alt="Bar chart of golden-hour duration by city at the March equinox, ordered by latitude, rising from 24 minutes at Singapore near the equator to 39.6 minutes at Berlin, the highest-latitude city in the set" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shortest at the equinox, longest in winter — and the swing grows with latitude
&lt;/h2&gt;

&lt;p&gt;Near the equator the swing is barely there: Singapore moves from about 24 minutes at the equinox to 26 at either solstice, under three minutes across the whole year. London, at 51.5°N, swings much further — about 39 minutes at the equinox stretching to roughly 55 minutes at the December solstice. Sydney sits in the southern hemisphere, so its calendar flips: golden hour peaks at Sydney's own winter solstice in June (about 34 minutes), not in December. The pattern holds throughout this dataset — higher latitude means a bigger seasonal swing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vb7q3kcrdt3iw9jax7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vb7q3kcrdt3iw9jax7u.png" alt="Grouped bar chart of golden-hour duration for Singapore, London, and Sydney at the March equinox, June solstice, and December solstice, showing Singapore nearly flat year-round while London and Sydney swing much more, peaking in their respective winter solstices" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why latitude decides golden-hour length
&lt;/h2&gt;

&lt;p&gt;Near the equator the sun drops toward the horizon almost vertically, so it crosses the +6°→0° elevation band quickly — golden hour in Singapore lasts about 24 minutes at the equinox and barely moves across the year. Toward the poles the sun descends at a shallower angle, so the same band takes longer to cross. London's equinox golden hour runs about 39 minutes, stretching to roughly 55 minutes at the December solstice; Berlin, at a similar latitude, peaks near 58 minutes in December — the longest in this dataset.&lt;/p&gt;

&lt;p&gt;The low winter sun is why the winter solstice produces the longest golden hour of the year in this dataset, not midsummer — a small margin near the equator, where the seasonal swing is barely a minute, but a gap of several minutes at higher latitudes, like London's roughly 55 minutes in December against 47 in June. Even so, every city here — up to Berlin's 52.5°N — stays under an hour. Golden hours that genuinely exceed 60 minutes need latitudes above roughly 60°N, places like Reykjavík or Anchorage, beyond the reach of this 27-city set.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full 27-city dataset
&lt;/h2&gt;

&lt;p&gt;Every figure below is minutes of golden hour, evening, sun between +6° and the horizon.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;City&lt;/th&gt;
&lt;th&gt;Lat&lt;/th&gt;
&lt;th&gt;Equinox&lt;/th&gt;
&lt;th&gt;Jun solstice&lt;/th&gt;
&lt;th&gt;Dec solstice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Singapore&lt;/td&gt;
&lt;td&gt;1.35°&lt;/td&gt;
&lt;td&gt;24 min&lt;/td&gt;
&lt;td&gt;26.2 min&lt;/td&gt;
&lt;td&gt;26.2 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manila&lt;/td&gt;
&lt;td&gt;14.6°&lt;/td&gt;
&lt;td&gt;24.8 min&lt;/td&gt;
&lt;td&gt;27 min&lt;/td&gt;
&lt;td&gt;27.4 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bangkok&lt;/td&gt;
&lt;td&gt;13.76°&lt;/td&gt;
&lt;td&gt;24.7 min&lt;/td&gt;
&lt;td&gt;26.9 min&lt;/td&gt;
&lt;td&gt;27.3 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyderabad&lt;/td&gt;
&lt;td&gt;17.39°&lt;/td&gt;
&lt;td&gt;25.2 min&lt;/td&gt;
&lt;td&gt;27.5 min&lt;/td&gt;
&lt;td&gt;27.9 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mexico City&lt;/td&gt;
&lt;td&gt;19.43°&lt;/td&gt;
&lt;td&gt;25.5 min&lt;/td&gt;
&lt;td&gt;27.8 min&lt;/td&gt;
&lt;td&gt;28.4 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hong Kong&lt;/td&gt;
&lt;td&gt;22.32°&lt;/td&gt;
&lt;td&gt;26 min&lt;/td&gt;
&lt;td&gt;28.4 min&lt;/td&gt;
&lt;td&gt;29.1 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Riyadh&lt;/td&gt;
&lt;td&gt;24.71°&lt;/td&gt;
&lt;td&gt;26.4 min&lt;/td&gt;
&lt;td&gt;29 min&lt;/td&gt;
&lt;td&gt;29.8 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dubai&lt;/td&gt;
&lt;td&gt;25.2°&lt;/td&gt;
&lt;td&gt;26.5 min&lt;/td&gt;
&lt;td&gt;29.2 min&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Miami&lt;/td&gt;
&lt;td&gt;25.76°&lt;/td&gt;
&lt;td&gt;26.7 min&lt;/td&gt;
&lt;td&gt;29.3 min&lt;/td&gt;
&lt;td&gt;30.2 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Houston&lt;/td&gt;
&lt;td&gt;29.76°&lt;/td&gt;
&lt;td&gt;27.7 min&lt;/td&gt;
&lt;td&gt;30.6 min&lt;/td&gt;
&lt;td&gt;31.7 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atlanta&lt;/td&gt;
&lt;td&gt;33.75°&lt;/td&gt;
&lt;td&gt;28.9 min&lt;/td&gt;
&lt;td&gt;32.2 min&lt;/td&gt;
&lt;td&gt;33.7 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Los Angeles&lt;/td&gt;
&lt;td&gt;34.05°&lt;/td&gt;
&lt;td&gt;29 min&lt;/td&gt;
&lt;td&gt;32.4 min&lt;/td&gt;
&lt;td&gt;33.9 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokyo&lt;/td&gt;
&lt;td&gt;35.68°&lt;/td&gt;
&lt;td&gt;29.6 min&lt;/td&gt;
&lt;td&gt;33.2 min&lt;/td&gt;
&lt;td&gt;34.8 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lisbon&lt;/td&gt;
&lt;td&gt;38.72°&lt;/td&gt;
&lt;td&gt;30.8 min&lt;/td&gt;
&lt;td&gt;34.8 min&lt;/td&gt;
&lt;td&gt;37 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Madrid&lt;/td&gt;
&lt;td&gt;40.42°&lt;/td&gt;
&lt;td&gt;31.6 min&lt;/td&gt;
&lt;td&gt;35.9 min&lt;/td&gt;
&lt;td&gt;38.4 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New York&lt;/td&gt;
&lt;td&gt;40.71°&lt;/td&gt;
&lt;td&gt;31.7 min&lt;/td&gt;
&lt;td&gt;36.1 min&lt;/td&gt;
&lt;td&gt;38.6 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Istanbul&lt;/td&gt;
&lt;td&gt;41.01°&lt;/td&gt;
&lt;td&gt;31.9 min&lt;/td&gt;
&lt;td&gt;36.3 min&lt;/td&gt;
&lt;td&gt;38.9 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chicago&lt;/td&gt;
&lt;td&gt;41.88°&lt;/td&gt;
&lt;td&gt;32.3 min&lt;/td&gt;
&lt;td&gt;37 min&lt;/td&gt;
&lt;td&gt;39.7 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toronto&lt;/td&gt;
&lt;td&gt;43.65°&lt;/td&gt;
&lt;td&gt;33.2 min&lt;/td&gt;
&lt;td&gt;38.4 min&lt;/td&gt;
&lt;td&gt;41.6 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seattle&lt;/td&gt;
&lt;td&gt;47.61°&lt;/td&gt;
&lt;td&gt;35.7 min&lt;/td&gt;
&lt;td&gt;42.2 min&lt;/td&gt;
&lt;td&gt;46.9 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paris&lt;/td&gt;
&lt;td&gt;48.86°&lt;/td&gt;
&lt;td&gt;36.6 min&lt;/td&gt;
&lt;td&gt;43.6 min&lt;/td&gt;
&lt;td&gt;49.1 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;London&lt;/td&gt;
&lt;td&gt;51.51°&lt;/td&gt;
&lt;td&gt;38.7 min&lt;/td&gt;
&lt;td&gt;47.2 min&lt;/td&gt;
&lt;td&gt;54.8 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amsterdam&lt;/td&gt;
&lt;td&gt;52.37°&lt;/td&gt;
&lt;td&gt;39.5 min&lt;/td&gt;
&lt;td&gt;48.6 min&lt;/td&gt;
&lt;td&gt;57.1 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Berlin&lt;/td&gt;
&lt;td&gt;52.52°&lt;/td&gt;
&lt;td&gt;39.6 min&lt;/td&gt;
&lt;td&gt;48.8 min&lt;/td&gt;
&lt;td&gt;57.6 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sydney&lt;/td&gt;
&lt;td&gt;-33.87°&lt;/td&gt;
&lt;td&gt;28.9 min&lt;/td&gt;
&lt;td&gt;33.7 min&lt;/td&gt;
&lt;td&gt;32.3 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buenos Aires&lt;/td&gt;
&lt;td&gt;-34.6°&lt;/td&gt;
&lt;td&gt;29.2 min&lt;/td&gt;
&lt;td&gt;34.2 min&lt;/td&gt;
&lt;td&gt;32.6 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auckland&lt;/td&gt;
&lt;td&gt;-36.85°&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;td&gt;35.6 min&lt;/td&gt;
&lt;td&gt;33.8 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Reproduce it yourself
&lt;/h2&gt;

&lt;p&gt;The harness is deterministic and public. It's one script — the NOAA low-precision solar-position algorithm — run against the same 27 cities and three dates (March equinox, June &amp;amp; December solstices, 2026), printing golden hour as the time the sun spends between +6° elevation and the horizon, evening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gist.github.com/lucian-devops/7681a587034398de14f8b408c3882583" rel="noopener noreferrer"&gt;Golden-hour harness (public gist)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every number on this page is printed by that one script. Run it yourself to check any figure above to the exact digit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The interactive charts and per-city live local time are on &lt;a href="https://lkforge.com/blog/how-long-is-golden-hour/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>photography</category>
      <category>data</category>
      <category>javascript</category>
      <category>science</category>
    </item>
    <item>
      <title>The ENABLE Lexicon Study — Letter Frequency vs Scrabble Tile Values</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Sat, 25 Jul 2026 11:03:59 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/the-enable-lexicon-study-letter-frequency-vs-scrabble-tile-values-186e</link>
      <guid>https://dev.to/lucian_lkb_1f009d/the-enable-lexicon-study-letter-frequency-vs-scrabble-tile-values-186e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This was originally published on &lt;a href="https://lkforge.com/blog/enable-lexicon-study/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;, where the charts are interactive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Scrabble's tile values were set in the 1930s by counting letters on a newspaper page. We counted every letter across all 172,835 words in the &lt;strong&gt;ENABLE open word list&lt;/strong&gt; — not the TWL or CSW/SOWPODS Scrabble dictionaries, just the open list — to see how well those 90-year-old values still line up with usage. They don't, in eleven places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C is the 10th-most-common letter in English word-stock, yet Scrabble scores it 3 points&lt;/strong&gt; — more than the 1-point U it outnumbers. Eleven letters carry a tile value that doesn't line up with how often they actually appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ENABLE words analyzed&lt;/td&gt;
&lt;td&gt;172,835&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tile value / frequency inversions&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Letters in the longest isograms&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;More words start with S than X&lt;/td&gt;
&lt;td&gt;139×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How often each letter actually appears
&lt;/h2&gt;

&lt;p&gt;Frequency here is occurrences per 1,000 letters: every letter of every word counted, divided by the total letter count, times a thousand. E leads at 115.1 per 1,000, S is second at 95.2, I third at 90.1. Q and J tie for last at 1.6.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fww78zvwk4i9vq0pjrtt8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fww78zvwk4i9vq0pjrtt8.png" alt="Bar chart of letter frequency per 1,000 letters across all 172,835 ENABLE words, ranked from E (115.1) down to the rare Q and J (1.6), bars colored by Scrabble tile value. Red arcs mark all 11 tile-value inversions — each a higher-value letter that is more common than a lower-value one it links to, e.g. C outranks D, G and U" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where value and frequency diverge
&lt;/h2&gt;

&lt;p&gt;Butts set higher values for rarer letters, so by design tile value roughly tracks how uncommon a letter is — a rough stand-in for how hard it is to play. Across the full ENABLE list that alignment holds at the extremes (E is common and worth 1; Q and Z are rare and worth 10) but breaks down in the middle. The harness finds &lt;strong&gt;all 11 inversions&lt;/strong&gt; — every case where a higher-value letter is actually more common than a lower-value one. Here is the complete list:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;More common (tile value)&lt;/th&gt;
&lt;th&gt;Rarer, but lower-value (tile value)&lt;/th&gt;
&lt;th&gt;Frequency (per 1,000 letters)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;H (4 pt)&lt;/td&gt;
&lt;td&gt;B (3 pt)&lt;/td&gt;
&lt;td&gt;H 23.2 vs B 18.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C (3 pt)&lt;/td&gt;
&lt;td&gt;D (2 pt)&lt;/td&gt;
&lt;td&gt;C 40.9 vs D 33.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C (3 pt)&lt;/td&gt;
&lt;td&gt;G (2 pt)&lt;/td&gt;
&lt;td&gt;C 40.9 vs G 27.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C (3 pt)&lt;/td&gt;
&lt;td&gt;U (1 pt)&lt;/td&gt;
&lt;td&gt;C 40.9 vs U 32.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D (2 pt)&lt;/td&gt;
&lt;td&gt;U (1 pt)&lt;/td&gt;
&lt;td&gt;D 33.8 vs U 32.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M (3 pt)&lt;/td&gt;
&lt;td&gt;G (2 pt)&lt;/td&gt;
&lt;td&gt;M 28.4 vs G 27.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P (3 pt)&lt;/td&gt;
&lt;td&gt;G (2 pt)&lt;/td&gt;
&lt;td&gt;P 29.4 vs G 27.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;K (5 pt)&lt;/td&gt;
&lt;td&gt;W (4 pt)&lt;/td&gt;
&lt;td&gt;K 8.5 vs W 7.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q (10 pt)&lt;/td&gt;
&lt;td&gt;J (8 pt)&lt;/td&gt;
&lt;td&gt;Q vs J — 2,536 vs 2,497 uses*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Z (10 pt)&lt;/td&gt;
&lt;td&gt;J (8 pt)&lt;/td&gt;
&lt;td&gt;Z 4.7 vs J 1.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Z (10 pt)&lt;/td&gt;
&lt;td&gt;X (8 pt)&lt;/td&gt;
&lt;td&gt;Z 4.7 vs X 2.9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* Q and J both round to 1.6 per 1,000 letters, so the ranking uses exact counts: Q edges J on raw usage (2,536 vs 2,497) across the 172,835 words.&lt;/p&gt;

&lt;p&gt;The single cleanest example is H against B: H is worth a full point more, but shows up more often across the lexicon. If tile values were re-derived from ENABLE frequencies today, several 3- and 4-point consonants — C, H, M, P — would come out lower.&lt;/p&gt;

&lt;h2&gt;
  
  
  The longest words with no repeated letter
&lt;/h2&gt;

&lt;p&gt;An isogram is a word in which no letter repeats. The longest in ENABLE are exactly 15 letters — and there are only two of them: &lt;strong&gt;dermatoglyphics&lt;/strong&gt; and &lt;strong&gt;uncopyrightable&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The most anagram-rich letters
&lt;/h2&gt;

&lt;p&gt;Group every word by its sorted letters and one set wins: the five letters &lt;strong&gt;A, E, P, R, S&lt;/strong&gt; rearrange into 12 different ENABLE words — apers, apres, asper, pares, parse, pears, prase, presa, rapes, reaps, spare, spear.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letters&lt;/th&gt;
&lt;th&gt;Words&lt;/th&gt;
&lt;th&gt;The words&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A E P R S&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;apers, apres, asper, pares, parse, pears, prase, presa, rapes, reaps, spare, spear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A E L R S T&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;alerts, alters, artels, estral, laster, ratels, salter, slater, staler, stelar, talers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A E L S T&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;least, setal, slate, stale, steal, stela, taels, tales, teals, tesla&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A E I N R S T&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;anestri, antsier, nastier, ratines, retains, retinas, retsina, stainer, stearin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Which letter starts the most words
&lt;/h2&gt;

&lt;p&gt;S runs away with it: 18,988 ENABLE words start with S. At the other end, only 136 start with X — a spread of roughly &lt;strong&gt;139 to 1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjxd92iwa5nus3ff174d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjxd92iwa5nus3ff174d.png" alt="Bar chart of how many ENABLE words start with each letter, ranked from S with 18,988 down to X with 136, S and X highlighted" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Scrabble reference
&lt;/h2&gt;

&lt;p&gt;SCRABBLE is referenced here only descriptively, as the source of the standard English tile-value set — a public reference point for comparison, not an official Scrabble analysis. SCRABBLE is a trademark of its respective owners; this post is not affiliated with, endorsed by, or sponsored by the trademark holder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it yourself
&lt;/h2&gt;

&lt;p&gt;The harness is public and deterministic — one script against the shipped ENABLE list (172,835 words), printing frequency per 1,000 letters, tile-value inversions, isograms, anagram-richest sets, and starting-letter counts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gist.github.com/lucian-devops/6c70c2cbe9299f8ede6f31f751554e30" rel="noopener noreferrer"&gt;ENABLE lexicon harness (public gist)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every number on this page is printed by that one script. Run it yourself to check any figure above to the exact digit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The interactive charts are on &lt;a href="https://lkforge.com/blog/enable-lexicon-study/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordgames</category>
      <category>scrabble</category>
      <category>data</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Find &amp; Learn the Constellations: A Beginner's Guide</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Thu, 23 Jul 2026 17:34:24 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/how-to-find-learn-the-constellations-a-beginners-guide-ofm</link>
      <guid>https://dev.to/lucian_lkb_1f009d/how-to-find-learn-the-constellations-a-beginners-guide-ofm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This guide was originally published on &lt;strong&gt;&lt;a href="https://lkforge.com/guides/how-to-find-the-constellations/" rel="noopener noreferrer"&gt;LK Forge&lt;/a&gt;&lt;/strong&gt;, where it links to a free interactive sky map and a live planetarium you can match to tonight's sky.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The constellations can feel like a secret code — dozens of faint patterns scattered across the sky with no obvious place to start. The trick is that you don't learn them one by one. You learn a handful of bright, unmistakable &lt;strong&gt;signpost&lt;/strong&gt; shapes first, then use them to &lt;strong&gt;star-hop&lt;/strong&gt; to everything else. This guide shows you the signposts, the hops that connect them, and which constellations are up in each season — all with the naked eye, no telescope required.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Learn just three signpost constellations first — the &lt;strong&gt;Big Dipper&lt;/strong&gt;, &lt;strong&gt;Orion&lt;/strong&gt; and &lt;strong&gt;Cassiopeia&lt;/strong&gt; — and you can star-hop your way to almost everything else in the night sky.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  You don't need a telescope
&lt;/h2&gt;

&lt;p&gt;Constellations are big — many stretch across a span of sky wider than your outstretched hand. A telescope shows only a tiny patch at high magnification, which is exactly the wrong tool for spotting a large pattern. Your eyes are perfect for the job. To give them the best chance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let your eyes &lt;strong&gt;dark-adapt&lt;/strong&gt; for 15–20 minutes — avoid looking at a bright phone screen during that time.&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;red light&lt;/strong&gt; (or your phone's red-tint mode) to read a map without ruining your night vision.&lt;/li&gt;
&lt;li&gt;Get away from direct streetlights if you can. Light pollution hides the faint stars, but the bright signpost stars punch through almost anywhere.&lt;/li&gt;
&lt;li&gt;A darker sky helps, but you can learn the brightest constellations even from a city.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start with a few signpost constellations
&lt;/h2&gt;

&lt;p&gt;These three are the anchors most stargazers learn first. Once you can find them without thinking, the rest of the sky opens up.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Big Dipper (Ursa Major)
&lt;/h3&gt;

&lt;p&gt;Seven bright stars in the shape of a saucepan or plough. From most northern latitudes it never sets, so it's visible on almost any clear night — just at different angles through the year. It's the launch-pad for more star-hops than any other pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orion
&lt;/h3&gt;

&lt;p&gt;The standout of the northern winter (and southern summer) evening sky. Look for &lt;strong&gt;three bright stars in a short, straight row&lt;/strong&gt; — Orion's Belt — flanked by the brilliant stars Betelgeuse (reddish, upper-left) and Rigel (blue-white, lower-right). Nothing else in the sky looks quite like it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cassiopeia
&lt;/h3&gt;

&lt;p&gt;A distinctive &lt;strong&gt;"W" (or "M")&lt;/strong&gt; of five stars, sitting on the opposite side of the North Star from the Big Dipper. Like the Dipper, it's visible year-round from northern latitudes, so when the Dipper is low, Cassiopeia is high.&lt;/p&gt;

&lt;h2&gt;
  
  
  Star-hopping: how to find everything else
&lt;/h2&gt;

&lt;p&gt;Star-hopping means drawing an imaginary line or curve through stars you already know to point at ones you don't. A few classics do most of the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Find the North Star:&lt;/strong&gt; the two stars at the end of the Big Dipper's "bowl" (Dubhe and Merak) are the &lt;em&gt;pointer stars&lt;/em&gt;. Draw a line up through them and it lands on &lt;strong&gt;Polaris&lt;/strong&gt;, the North Star — the tip of the Little Dipper's handle (Ursa Minor).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arc to Arcturus, then speed on to Spica:&lt;/strong&gt; follow the curve of the Big Dipper's handle outward and it "arcs" to &lt;strong&gt;Arcturus&lt;/strong&gt;, a brilliant orange star in Boötes. Keep going the same distance and you "speed on to &lt;strong&gt;Spica&lt;/strong&gt;," the bright star of Virgo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From Orion's Belt:&lt;/strong&gt; extend the belt down and to the left to reach &lt;strong&gt;Sirius&lt;/strong&gt;, the brightest star in the night sky (in Canis Major). Extend it up and to the right to reach orange &lt;strong&gt;Aldebaran&lt;/strong&gt; in Taurus, and keep going to find the tiny, sparkling &lt;strong&gt;Pleiades&lt;/strong&gt; star cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Summer Triangle:&lt;/strong&gt; on summer evenings, three bright stars high overhead — &lt;strong&gt;Vega&lt;/strong&gt;, &lt;strong&gt;Deneb&lt;/strong&gt; and &lt;strong&gt;Altair&lt;/strong&gt; — form a large triangle spanning the constellations Lyra, Cygnus and Aquila.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which constellations are up? A season-by-season guide
&lt;/h2&gt;

&lt;p&gt;The sky's cast changes through the year as Earth orbits the Sun, so different constellations dominate the evening sky each season. The table below is for the &lt;strong&gt;Northern Hemisphere, a few hours after sunset&lt;/strong&gt;. Visibility shifts with your latitude, and in the &lt;strong&gt;Southern Hemisphere the seasons are reversed&lt;/strong&gt; and many patterns appear "upside down."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Season (N. Hemisphere)&lt;/th&gt;
&lt;th&gt;Prominent constellations&lt;/th&gt;
&lt;th&gt;How to find them&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spring&lt;/td&gt;
&lt;td&gt;Leo, Boötes, Virgo, Ursa Major (high overhead)&lt;/td&gt;
&lt;td&gt;Arc off the Big Dipper's handle to Arcturus (Boötes), then on to Spica (Virgo). Leo's backwards question-mark "Sickle" sits nearby.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Summer&lt;/td&gt;
&lt;td&gt;Cygnus, Lyra, Aquila, Scorpius, Sagittarius&lt;/td&gt;
&lt;td&gt;The Summer Triangle (Vega, Deneb, Altair) rides high; Scorpius and Sagittarius sit low toward the south over the Milky Way.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autumn&lt;/td&gt;
&lt;td&gt;Pegasus, Andromeda, Cassiopeia, Perseus&lt;/td&gt;
&lt;td&gt;Find the Great Square of Pegasus; Cassiopeia's "W" is high overhead, with the Andromeda Galaxy nearby.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Winter&lt;/td&gt;
&lt;td&gt;Orion, Taurus, Gemini, Canis Major, Auriga&lt;/td&gt;
&lt;td&gt;Orion's Belt points down-left to Sirius (Canis Major) and up-right to Aldebaran (Taurus). The brightest patch of sky all year.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Tips to actually remember the constellations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learn a few at a time.&lt;/strong&gt; Two or three new shapes per session sticks far better than trying to memorise a whole star chart at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go out at the same time each week.&lt;/strong&gt; You'll watch the same constellations drift a little westward night to night, which cements where they live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with the brightest.&lt;/strong&gt; The signpost stars and patterns show even in poor skies; save the faint constellations for darker nights once you have your bearings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use averted vision.&lt;/strong&gt; To catch a faint star or cluster, look slightly to one side of it — the edges of your eyes are more light-sensitive than the centre.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on the constellation lines.&lt;/strong&gt; A planetarium view that draws the constellation figures and labels over the real sky lets you check a shape the moment you spot it — then turn the lines off and test yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How many constellations are there?&lt;/strong&gt; There are 88 official constellations, defined by the International Astronomical Union (IAU). They divide the entire sky into 88 regions, so every star belongs to exactly one constellation. Only a few dozen are prominent enough to pick out easily by eye.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need a telescope?&lt;/strong&gt; No. Constellations are large patterns, so they are best seen with the naked eye — a telescope shows too small a patch to take one in. It only helps later, for zooming in on individual objects like the Moon, planets or star clusters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the easiest constellation to find?&lt;/strong&gt; For most of the Northern Hemisphere it is the Big Dipper — seven bright stars in a saucepan shape, visible most of the year. In northern winter, Orion is just as easy thanks to its belt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match this to the real sky
&lt;/h2&gt;

&lt;p&gt;The fastest way to turn this into muscle memory is to compare it with the real sky. Open a sky map, find the signposts overhead right now, and trace the same star-hops on screen and above you. The free &lt;strong&gt;&lt;a href="https://lkforge.com/tools/space/sky/" rel="noopener noreferrer"&gt;LK Forge Sky Explorer&lt;/a&gt;&lt;/strong&gt; gives you an interactive deep-sky map to locate any object, plus a live planetarium view (powered by Stellarium Web) that &lt;strong&gt;draws the constellation lines and labels straight over the real sky for your location and time&lt;/strong&gt; — so the exact patterns in this guide are marked out overhead right now. No account or sign-up.&lt;/p&gt;

</description>
      <category>astronomy</category>
      <category>science</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Proving It's Actually Unbeatable: How I Benchmark a Game AI Before Publishing a Number</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Mon, 20 Jul 2026 19:51:00 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/proving-its-actually-unbeatable-how-i-benchmark-a-game-ai-before-publishing-a-number-5009</link>
      <guid>https://dev.to/lucian_lkb_1f009d/proving-its-actually-unbeatable-how-i-benchmark-a-game-ai-before-publishing-a-number-5009</guid>
      <description>&lt;p&gt;This was originally published on &lt;a href="https://lkforge.com/blog/benchmarking-game-ai/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;, where the charts are interactive and you can play the AI it talks about.&lt;/p&gt;

&lt;p&gt;"Unbeatable" is a testable claim, not a marketing word. Before that word goes anywhere near the &lt;a href="https://lkforge.com/games/tictactoe/" rel="noopener noreferrer"&gt;tic-tac-toe page&lt;/a&gt;, the shipped AI has to survive a headless self-play harness — the same functions the browser runs, played thousands of times with no human at the keyboard. Here is what that harness measured, including the part that didn't look good at first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The counterintuitive result
&lt;/h2&gt;

&lt;p&gt;A tic-tac-toe AI with an &lt;strong&gt;87% win rate still lost games&lt;/strong&gt; that the full-depth engine never would. "It's minimax, so it's unbeatable" is true at exactly one depth setting — and the only way to find which is to run every depth and count the losses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tic-tac-toe games simulated&lt;/td&gt;
&lt;td&gt;1,200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Losses recorded&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2048 self-play runs&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nodes / opening (α-β)&lt;/td&gt;
&lt;td&gt;36,528&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Against 1,000 games of a random-move opponent, the AI went 825 wins, 175 draws, &lt;strong&gt;0 losses&lt;/strong&gt;. Against 200 games of the AI playing a perfect copy of itself, every game ended in a draw — the correct result for perfect tic-tac-toe play, and the harder claim to satisfy. Across both runs, 1,200 games total, the loss count is 0. That is the number the word "unbeatable" is actually standing on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The harness
&lt;/h2&gt;

&lt;p&gt;The benchmark is headless self-play: the exact move-selection functions from the shipped page, imported into a script with no DOM and no human, playing thousands of games back to back. A benchmark written against a &lt;em&gt;re-implementation&lt;/em&gt; of the algorithm proves the algorithm is sound in theory; it says nothing about the code a browser actually executes. Pulling the real functions out of the production page means the numbers describe the shipped build, not a clean-room stand-in that happens to share a name with it.&lt;/p&gt;

&lt;p&gt;It also has to be re-runnable. A number from one afternoon of manual play-testing is an anecdote; a script that plays 1,000 games and produces the same counts every time is a measurement. Mean move time came out at about 0.3 ms across 500 sampled positions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Depth is the proof
&lt;/h2&gt;

&lt;p&gt;The shipped engine searches to depth 9 — the full game tree for a 3×3 board — which is what guarantees the zero-loss result. To find out whether that depth is &lt;em&gt;necessary&lt;/em&gt;, the harness re-ran the same 200-game vs-random test with the search capped at each depth from 1 to 9:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr7tygw81ppfh508979j5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr7tygw81ppfh508979j5.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Search depth&lt;/th&gt;
&lt;th&gt;Win % (vs random, 200 games)&lt;/th&gt;
&lt;th&gt;Losses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;86%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;87%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;90.5%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;86%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;83.5%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;77%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;85%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;79%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 (shipped)&lt;/td&gt;
&lt;td&gt;81%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The honest finding is in the depth-2 row: at a search depth of 2, the AI &lt;strong&gt;lost 2 of 200 games&lt;/strong&gt;. An 87% win rate looks fine on its own, until you notice the two losses hiding next to it. Depth 9 is the only depth with a 0-loss guarantee behind it; every shallower depth is a different, weaker program that happens to share the same UI.&lt;/p&gt;

&lt;p&gt;That is the entire argument for benchmarking instead of reasoning from the algorithm's name. "It's minimax, so it's unbeatable" is true only at one specific depth setting, and the only way to know which is to run all of them and look for the losses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2048 case
&lt;/h2&gt;

&lt;p&gt;Tic-tac-toe has a small enough state space that "unbeatable" is checkable in an absolute sense. Most games don't — &lt;a href="https://lkforge.com/games/2048/" rel="noopener noreferrer"&gt;2048&lt;/a&gt; has no realistic path to "solved," so the only honest claim is a measured reach-rate: 250 headless self-play games with the shipped expectimax-plus-corner-snake solver.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6zowns1q2ocvo12w52xq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6zowns1q2ocvo12w52xq.png" alt=" " width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A strength profile, not a single number — and one that would be indistinguishable from a coin flip if I'd run it 10 times instead of 250.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it yourself
&lt;/h2&gt;

&lt;p&gt;The harness is public and seeded, so runs are deterministic. Each file copies the shipped game code verbatim and drives it headlessly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gist.github.com/lucian-devops/3de1641f4adb831ff9b8827acd8eb6d5" rel="noopener noreferrer"&gt;The benchmark harness (public gist)&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;node tictactoe-benchmark.mjs   #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;549,945 -&amp;gt; 36,528 nodes, 0 losses, depth-2 loses 2
&lt;span class="gp"&gt;node 2048-benchmark.mjs        #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;reach-rate profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deterministic numbers (node counts, 0 losses at full depth, "only depth 2 loses") reproduce to the exact digit. The stochastic ones (win/draw split, 2048 reach %) reproduce as the same profile with seed-dependent variance. I also included the one number that &lt;em&gt;didn't&lt;/em&gt; cleanly reproduce — a Color Lines pathfinding stat that turned out to be board-sampling-dependent — and documented why, rather than curve-fitting to hit it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule this enforces
&lt;/h2&gt;

&lt;p&gt;Benchmark before you publish a number. Any performance or strength claim has to come from a measurement that can be re-run, not a description of what the algorithm should do in theory. The depth-2 result is exactly the kind of thing that rule is for — a plausible-sounding claim ("it's minimax, it can't lose") that turns out to be depth-dependent, and would have shipped wrong without a script that actually played the games.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>26 Repos in 29 Days With an AI Pipeline: What Actually Broke</title>
      <dc:creator>Lucian (LKB)</dc:creator>
      <pubDate>Sun, 12 Jul 2026 14:20:35 +0000</pubDate>
      <link>https://dev.to/lucian_lkb_1f009d/26-repos-in-29-days-with-an-ai-pipeline-what-actually-broke-4jlm</link>
      <guid>https://dev.to/lucian_lkb_1f009d/26-repos-in-29-days-with-an-ai-pipeline-what-actually-broke-4jlm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This was originally published on &lt;a href="https://lkforge.com/blog/26-repos-in-29-days/" rel="noopener noreferrer"&gt;the LK Forge blog&lt;/a&gt;, where the commit chart is interactive and you can play the AI games it talks about.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Between June 5 and July 3, 2026, I took an empty domain to &lt;strong&gt;26 repositories, 1,549 commits, and 335 live pages&lt;/strong&gt; — one developer, working with Claude Code. The interesting part isn't the volume. It's &lt;em&gt;which&lt;/em&gt; failure modes showed up, because none of them were the ones the AI-coding debate argues about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Repositories&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commits&lt;/td&gt;
&lt;td&gt;1,549&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pages shipped&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Commit cadence: &lt;strong&gt;~53/day average&lt;/strong&gt;, &lt;strong&gt;peak 122 on June 20&lt;/strong&gt;, exactly &lt;strong&gt;one&lt;/strong&gt; zero-commit day in the sprint. The two heaviest days at the tail (114 each) were a site-wide URL-structure migration — which is itself one of the failure stories below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Deliberately boring: one developer, Claude Code in the terminal, git for everything, static-first architecture. Games and tools are self-contained repos of vanilla HTML/CSS/JS; the hub site is Astro. Everything deploys to Cloudflare Workers with static assets at the edge — no backend, no database, nothing to babysit at 3am.&lt;/p&gt;

&lt;p&gt;Every session ran the same loop: describe the goal, let the model plan and build, review the diff, make it verify its own work against the live site, commit. That verification step is what earns its keep — every failure below was caught by a check, not by luck.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the pipeline was genuinely good at
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Working classical-AI engines, first try or close.&lt;/strong&gt; The 2048 solver is real expectimax search with a corner-snake heuristic and adaptive depth. Before publishing any performance number, I ran the exact production code through 250 headless self-play games: it reaches the 2048 tile in &lt;strong&gt;69.6% of games at ~0.5ms per move&lt;/strong&gt;. The tic-tac-toe opponent is minimax with alpha-beta; the pathfinding in Color Lines is BFS. Textbook algorithms, correctly implemented, shipped in days. That part of the hype is real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Volume with consistency.&lt;/strong&gt; 335 pages sharing one brand system, one URL convention, one schema pattern. Once a convention was written into a project memory file, the model applied it across dozens of pages without drift. Those memory files turned out to be the highest-leverage artifact in the whole pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audits at a depth a human won't sustain.&lt;/strong&gt; Full link-graph crawls, redirect-chain verification across hundreds of URLs, per-page canonical checks against live HTTP. The model does the 400-URL tedium without getting bored — which matters, because tedium is where site-wide bugs hide.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually broke
&lt;/h2&gt;

&lt;p&gt;Not one failure was a syntax error, a broken build, or code that didn't run. Every real problem was &lt;strong&gt;structural&lt;/strong&gt; — invisible in any single diff, only visible when you look at the whole system.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The pipeline competed with itself
&lt;/h3&gt;

&lt;p&gt;Asked for a word-tools hub, the pipeline built one — at &lt;code&gt;/word-tools/&lt;/code&gt;, while the existing tools lived under &lt;code&gt;/tools/&lt;/code&gt;. Two pages on the same domain targeting the same queries: textbook SEO cannibalization, self-inflicted. Search Console showed both URLs impressing for the same terms before I consolidated with a 301. Each page was locally correct; nobody was watching the query-level picture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: the model optimizes the page you asked for, not the site you already have.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Twenty-six repos, two URL conventions, weeks of cleanup
&lt;/h3&gt;

&lt;p&gt;Some tools were built as flat files (&lt;code&gt;page.html&lt;/code&gt;), others as directories (&lt;code&gt;page/index.html&lt;/code&gt;). On Cloudflare's asset serving those get opposite trailing-slash behavior — so the site accumulated canonical mismatches, two-hop redirect chains, and Search Console redirect errors. The fix consumed the two biggest commit days of the sprint and produced a written URL convention plus a pre-deploy crawl checker that now gates every release.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: conventions the model must follow have to be written before repo #2, not after repo #20.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Source and production drifted apart silently
&lt;/h3&gt;

&lt;p&gt;Game repos are mirrored into the hub site for deployment. Over weeks, SEO improvements were applied to the production mirror and never back-ported to source. The trap armed itself: the obvious "sync" — copy source over mirror — would have silently destroyed live metadata. It was caught only because a diff-before-copy check is now mandatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: any two copies of the same file will diverge, and the AI won't notice unless a check forces the comparison.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The verification tools lied too
&lt;/h3&gt;

&lt;p&gt;The first link-graph audit reported a wave of orphaned pages. False alarm: it compared absolute URLs against unresolved relative hrefs. A later canonical audit reported 123 mismatches — also false, because the checker assumed file paths equal serving paths, and the CDN serves clean URLs. In both cases the &lt;em&gt;audit tooling&lt;/em&gt; — also AI-written — had the bug, and acting on its output would have "fixed" a healthy site into a broken one. Both caught the same way: probe the live site before believing static analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: verify the verifier. An AI-written check inherits every blind spot of the AI that wrote it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The economics: 93% of the cost was re-reading, not writing
&lt;/h2&gt;

&lt;p&gt;The sprint produced 44 working sessions and 826MB of transcripts. When I audited the token bill, the headline wasn't generation cost: &lt;strong&gt;roughly 93% of token consumption was cached context being re-read&lt;/strong&gt;, turn after turn, inside marathon sessions that should have been split up.&lt;/p&gt;

&lt;p&gt;The mechanics are mundane. A long session accumulates giant context; every subsequent turn re-reads it; a session that drifts across three unrelated tasks pays the full history of tasks one and two as a tax on task three. The model never complains, so nothing forces you to notice.&lt;/p&gt;

&lt;p&gt;The fix cost nothing: clear context between tasks, keep durable knowledge in small memory files, treat "one session = one task" as the default. If you run an AI coding workflow and have never audited where the tokens actually go, that single check is probably worth more than any prompt engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it earned (honest version)
&lt;/h2&gt;

&lt;p&gt;Over the last 28 days the site drew &lt;strong&gt;207 clicks from 7,320 impressions&lt;/strong&gt; across 257 pages with search data. For a domain about five weeks old, that's a normal, healthy trajectory — and nobody's growth-hack screenshot.&lt;/p&gt;

&lt;p&gt;The detail worth reporting: the single biggest click-earner after the homepage is the 2048 game with the visible AI solver — the page where the most genuine engineering lives. Search demand followed the depth, not the page count. 300 thin pages didn't beat one page with something real on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rules that survived
&lt;/h2&gt;

&lt;p&gt;Every one exists because its absence caused a real incident above. That's the only rule-making process that works.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark before you publish a number.&lt;/strong&gt; If it isn't measured, it doesn't ship.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diff before you copy.&lt;/strong&gt; No exceptions for "they should be identical."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probe live before believing static analysis.&lt;/strong&gt; An audit result is a hypothesis until production confirms it over HTTP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write conventions down before scaling them&lt;/strong&gt; — in a memory file the model loads every session, not in your head.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One session, one task.&lt;/strong&gt; Context is the real cost center.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the whole site, not the diff.&lt;/strong&gt; Cannibalization, drift, and convention splits are invisible at diff level.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;The live version, the interactive commit chart, and the AI games are at &lt;a href="https://lkforge.com/blog/26-repos-in-29-days/" rel="noopener noreferrer"&gt;lkforge.com&lt;/a&gt;. The 2048 solver writeup with the full benchmark is &lt;a href="https://lkforge.com/games/2048/blog-how-the-ai-solver-works" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
