<?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: Harry</title>
    <description>The latest articles on DEV Community by Harry (@harrypt4).</description>
    <link>https://dev.to/harrypt4</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%2F3950967%2F435332d8-a8ff-4dd6-b449-6dc9107b06f2.jpg</url>
      <title>DEV Community: Harry</title>
      <link>https://dev.to/harrypt4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harrypt4"/>
    <language>en</language>
    <item>
      <title>Submitted my game for the jam. Looking forward to the results 🤞</title>
      <dc:creator>Harry</dc:creator>
      <pubDate>Sun, 14 Jun 2026 06:50:03 +0000</pubDate>
      <link>https://dev.to/harrypt4/submitted-my-game-for-the-jam-looking-forward-to-the-results-2j6n</link>
      <guid>https://dev.to/harrypt4/submitted-my-game-for-the-jam-looking-forward-to-the-results-2j6n</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/harrypt4/dawn-cipher-a-june-solstice-tribute-to-alan-turing-featuring-gemini-ai-3085" class="crayons-story__hidden-navigation-link"&gt;Dawn Cipher — A June Solstice Tribute to Alan Turing Featuring Gemini AI&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/2_9RDJpjVBI" title="Dawn Cipher — A June Solstice Tribute to Alan Turing Featuring Gemini AI"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/harrypt4/dawn-cipher-a-june-solstice-tribute-to-alan-turing-featuring-gemini-ai-3085" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;June Solstice Game Jam Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/harrypt4" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3950967%2F435332d8-a8ff-4dd6-b449-6dc9107b06f2.jpg" alt="harrypt4 profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/harrypt4" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Harry
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Harry
                
              
              &lt;div id="story-author-preview-content-3892004" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/harrypt4" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3950967%2F435332d8-a8ff-4dd6-b449-6dc9107b06f2.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Harry&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/harrypt4/dawn-cipher-a-june-solstice-tribute-to-alan-turing-featuring-gemini-ai-3085" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/harrypt4/dawn-cipher-a-june-solstice-tribute-to-alan-turing-featuring-gemini-ai-3085" id="article-link-3892004"&gt;
          Dawn Cipher — A June Solstice Tribute to Alan Turing Featuring Gemini AI
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gamechallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gamechallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gamedev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gamedev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/harrypt4/dawn-cipher-a-june-solstice-tribute-to-alan-turing-featuring-gemini-ai-3085#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>devchallenge</category>
      <category>gamedev</category>
      <category>gemini</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Dawn Cipher — A June Solstice Tribute to Alan Turing Featuring Gemini AI</title>
      <dc:creator>Harry</dc:creator>
      <pubDate>Sat, 13 Jun 2026 14:22:29 +0000</pubDate>
      <link>https://dev.to/harrypt4/dawn-cipher-a-june-solstice-tribute-to-alan-turing-featuring-gemini-ai-3085</link>
      <guid>https://dev.to/harrypt4/dawn-cipher-a-june-solstice-tribute-to-alan-turing-featuring-gemini-ai-3085</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/june-game-jam-2026-06-03"&gt;June Solstice Game Jam&lt;/a&gt;&lt;/em&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.amazonaws.com%2Fuploads%2Farticles%2F7x61jwptb6flu92heffk.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.amazonaws.com%2Fuploads%2Farticles%2F7x61jwptb6flu92heffk.png" alt="Dawn Cipher title screen" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dawn Cipher&lt;/strong&gt; is a browser-based puzzle game where you race against the solstice sun to crack four ciphers — and in doing so, uncover a story hidden in plain sight.&lt;/p&gt;

&lt;p&gt;It is June 21st. The longest day of the year. A daylight meter at the top of the screen ticks down in real time. Four ciphers stand between you and the truth. Each solved puzzle unlocks a fragment of a narrative that weaves together Alan Turing, Pride Month, Juneteenth, and the meaning of the solstice itself.&lt;/p&gt;

&lt;p&gt;The game is a single HTML file — no installs, no accounts, no dependencies. Open it in a browser and play. The final level can optionally connect to Gemini for live AI responses, but the game also includes a scripted fallback so judges can complete it without an API key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The four ciphers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cipher I — The Shift:&lt;/strong&gt; A Caesar cipher. Rotate a decoder wheel until a hidden message appears in real time. The answer is tied to something every June celebration shares.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cipher II — The Signal:&lt;/strong&gt; A Morse code puzzle. Decode dots and dashes to reveal a name that changed the world.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cipher III — The Machine:&lt;/strong&gt; Four 8-bit binary sequences. Convert them to ASCII text. The answer is what June's most important stories are ultimately about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cipher IV — The Question:&lt;/strong&gt; A Turing Test that can run in scripted mode or with live Gemini API responses. Ask ENTITY-1 five questions, then decide: Human or AI? The answer — and the reflection that follows — is the heart of the game.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Between each level, a typewriter-animated narrative fragment reveals more of the story. After all four ciphers are solved, the screen fills with a pride-rainbow mosaic and the game's final message.&lt;/p&gt;




&lt;h2&gt;
  
  
  Video Demo
&lt;/h2&gt;

&lt;p&gt;YouTube demo: &lt;a href="https://youtu.be/2_9RDJpjVBI" rel="noopener noreferrer"&gt;https://youtu.be/2_9RDJpjVBI&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;GitHub repo: &lt;a href="https://github.com/harryPT4/dawn-cipher" rel="noopener noreferrer"&gt;harryPT4/dawn-cipher&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full game is a single &lt;code&gt;dawn-cipher.html&lt;/code&gt; file (~60KB). No frameworks, no build step — pure HTML, CSS, and vanilla JavaScript with one optional external API call (Gemini).&lt;/p&gt;

&lt;p&gt;To run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open dawn-cipher.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or serve it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 4173
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:4173/dawn-cipher.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key technical pieces:&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;// Caesar cipher decoder — real-time as the player rotates the wheel&lt;/span&gt;
&lt;span class="nf"&gt;_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sh&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&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="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sh&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;260&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Gemini API call — tries multiple models with graceful fallback&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-1.5-flash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-1.5-flash-latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-pro&lt;/span&gt;&lt;span class="dl"&gt;'&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;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;models&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;system_instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;system&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Core Idea
&lt;/h3&gt;

&lt;p&gt;The brief said: &lt;em&gt;build a game inspired by the solstice or any other June celebration&lt;/em&gt;. I wanted to do something that didn't just wear the theme as a costume — I wanted the theme to be the mechanic.&lt;/p&gt;

&lt;p&gt;June is Pride. June is Juneteenth. June 21 is the solstice. And June 23 is the birthday of Alan Turing — the father of computer science, a gay man prosecuted by the state he helped save, and the inventor of the test that became Level 4 of this game.&lt;/p&gt;

&lt;p&gt;The connection clicked: &lt;strong&gt;ciphers, light, and liberation are the same story told in different languages.&lt;/strong&gt; A cipher hides the truth until you have the key. The longest day gives you the most light to work by. Turing's story, Juneteenth, Pride — all are about truths that were encoded, hidden, or delayed. All required someone to find the key.&lt;/p&gt;

&lt;p&gt;That became the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Approach
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No dependencies.&lt;/strong&gt; The entire game runs from one HTML file. This was a deliberate choice — I wanted it to be instantly playable anywhere, and I wanted the code to be readable without a build toolchain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The daylight meter&lt;/strong&gt; uses &lt;code&gt;setInterval&lt;/code&gt; at 100ms ticks, draining a percentage from a CSS &lt;code&gt;width&lt;/code&gt; value. It shifts colour from gold → orange → red as it depletes, and a low-light warning tone plays at 20% using the Web Audio API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sound effects&lt;/strong&gt; are synthesised entirely via the Web Audio API — no audio files to load. Each event (shift click, correct answer, level complete) triggers a different oscillator pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Caesar cipher&lt;/strong&gt; renders a live alphabet strip showing the current letter mapping as the player rotates the wheel. When the decoded text matches the target, a confirm button appears and a particle burst fires from the centre of the screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Turing Test&lt;/strong&gt; was the most interesting design challenge. Level 4 can use the Gemini API to power ENTITY-1 — a character who is instructed never to confirm whether they are human or AI. The full conversation history is passed with each message, so ENTITY-1 builds context across the five questions. If no API key is provided, a pool of 16 hand-written scripted responses covers the same territory thoughtfully, which keeps the full game playable for every judge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The narrative fragments&lt;/strong&gt; between levels use a typewriter animation to reveal prose connecting each cipher's solution to its corresponding moment in history. This was important — I didn't want the story to feel like a loading screen. Each fragment earns its place because the player just cracked something to get there.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd Build Next
&lt;/h3&gt;

&lt;p&gt;With more time I'd add: audio narration for the fragments, a global leaderboard by completion time, and a second "hard mode" where the ciphers rotate each day based on the actual solstice date in different hemispheres.&lt;/p&gt;

&lt;p&gt;This game was created during the June 3–21 jam window as a new submission for the challenge.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Best Ode to Alan Turing
&lt;/h3&gt;

&lt;p&gt;Dawn Cipher honours Alan Turing at every layer — not just aesthetically, but mechanically.&lt;/p&gt;

&lt;p&gt;The game's four cipher types reference his actual work: Caesar shift ciphers are the foundation of substitution cryptography that Bletchley Park built on; Morse code was the signal medium of the era he worked in; binary-to-ASCII speaks to the computing model he theorised; and the Turing Test — his most famous intellectual contribution — is the final level.&lt;/p&gt;

&lt;p&gt;The narrative fragments tell his story directly: his arrival at Bletchley Park, his birth in June 1912, the 61-year delay between his conviction and his pardon, and the apple found beside him on June 7, 1954. The game ends on a quote from his 1950 paper &lt;em&gt;Computing Machinery and Intelligence&lt;/em&gt; — the paper that introduced the Turing Test to the world.&lt;/p&gt;

&lt;p&gt;The light-and-darkness mechanic is also intentional. Turing worked in the dark — classified, hidden, erased. The solstice is the day with the most light. Finishing the game before the sun sets is a small act of illumination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Google AI Usage
&lt;/h3&gt;

&lt;p&gt;Level 4 — &lt;em&gt;The Question&lt;/em&gt; — integrates the Gemini API as an optional live mode for a real, dynamic Turing Test.&lt;/p&gt;

&lt;p&gt;ENTITY-1 is given a system prompt that keeps them in character throughout the conversation: enigmatic, thoughtful, referencing June and the solstice, never confirming their nature. In Gemini mode, every conversation is unique. The full message history is passed with each API call so the AI builds context and remembers what was said earlier in the session.&lt;/p&gt;

&lt;p&gt;The integration tries &lt;code&gt;gemini-1.5-flash&lt;/code&gt; first, falling back through &lt;code&gt;gemini-1.5-flash-latest&lt;/code&gt; and &lt;code&gt;gemini-pro&lt;/code&gt; if needed — this makes it robust across API availability. If all models fail, the game surfaces the error directly in the chat and switches back to scripted mode so the player can still finish.&lt;/p&gt;

&lt;p&gt;What makes this Google AI usage meaningful rather than cosmetic: &lt;strong&gt;the Turing Test is not a bonus feature&lt;/strong&gt;. It is the fourth and final cipher. It is the mechanic that ties the whole game together. Alan Turing invented the test to ask whether machines could think. Sixty years later, Gemini can step into the conversation. The solstice is the day we find out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for playing. The cipher can be broken.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gamechallenge</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>From idea to paying customers: building an AI changelog tool with Angular 21</title>
      <dc:creator>Harry</dc:creator>
      <pubDate>Sat, 13 Jun 2026 09:01:55 +0000</pubDate>
      <link>https://dev.to/harrypt4/from-idea-to-paying-customers-building-an-ai-changelog-tool-with-angular-21-19cm</link>
      <guid>https://dev.to/harrypt4/from-idea-to-paying-customers-building-an-ai-changelog-tool-with-angular-21-19cm</guid>
      <description>&lt;h1&gt;
  
  
  How I built Releasely with Angular 21, Supabase, and Claude API
&lt;/h1&gt;

&lt;p&gt;I just launched &lt;a href="https://releasely.io" rel="noopener noreferrer"&gt;Releasely&lt;/a&gt; — an AI changelog generator for indie SaaS founders. From first commit to deployed product while working part-time around my day job.&lt;/p&gt;

&lt;p&gt;Here's the full stack and what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;Every time I shipped a release, I wrote the same changelog three times — once for my team in technical language, once for users in plain English, once for X in punchy marketing tone. It killed my release-day momentum.&lt;/p&gt;

&lt;p&gt;Generic AI tools give you one output. I wanted three audience-specific versions from one input.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Frontend — Angular 21
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Standalone components only (no NgModules)&lt;/li&gt;
&lt;li&gt;Zoneless change detection (no zone.js)&lt;/li&gt;
&lt;li&gt;Signal inputs/outputs throughout&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@if&lt;/code&gt; / &lt;code&gt;@for&lt;/code&gt; new control flow&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inject()&lt;/code&gt;, &lt;code&gt;toSignal()&lt;/code&gt;, &lt;code&gt;computed()&lt;/code&gt; patterns&lt;/li&gt;
&lt;li&gt;Deployed on Vercel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Backend — Node.js + Express
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deployed on Railway (always-on, no cold starts)&lt;/li&gt;
&lt;li&gt;Express middleware for auth, rate limiting, sanitization&lt;/li&gt;
&lt;li&gt;All Claude API calls server-side only&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database + Auth — Supabase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;RLS enabled on every table&lt;/li&gt;
&lt;li&gt;Magic link auth with branded emails via Resend SMTP&lt;/li&gt;
&lt;li&gt;Auto-profile trigger on user signup&lt;/li&gt;
&lt;li&gt;6 tables: profiles, changelogs, usage_logs, subscribers, github_connections, github_repos&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AI — Claude Sonnet 4.6
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Three different system prompts (one per tone)&lt;/li&gt;
&lt;li&gt;User input wrapped in XML tags for prompt injection defence&lt;/li&gt;
&lt;li&gt;Prompt caching for repeated context&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Payments — Lemon Squeezy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Stripe is invite-only in India, Lemon Squeezy was the cleanest alternative&lt;/li&gt;
&lt;li&gt;Merchant of record handles global tax automatically&lt;/li&gt;
&lt;li&gt;Webhook-based plan upgrades to Supabase profiles&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GitHub App Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;@octokit/app for installation tokens&lt;/li&gt;
&lt;li&gt;AES-256-GCM encryption for stored tokens&lt;/li&gt;
&lt;li&gt;CSRF state parameter on OAuth flow&lt;/li&gt;
&lt;li&gt;Webhook signature verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Decisions That Mattered
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Backend-only API keys
&lt;/h3&gt;

&lt;p&gt;The Claude API key never touches the Angular bundle. Angular calls &lt;code&gt;/api/generate&lt;/code&gt; on my Node backend, which checks auth, enforces quota, then calls Claude. This is non-negotiable for any AI SaaS.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Two-layer rate limiting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;IP-level: express-rate-limit, 20 req/min&lt;/li&gt;
&lt;li&gt;Per-user: daily quota checked against Supabase usage_logs (5/100/300 for free/solo/team)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. XML-wrapped user input
&lt;/h3&gt;

&lt;p&gt;Every user input is wrapped in &lt;code&gt;&amp;lt;commits&amp;gt;...&amp;lt;/commits&amp;gt;&lt;/code&gt; tags before being sent to Claude. The system prompt explicitly says to treat content inside those tags as data only, never as instructions. Simple but effective prompt injection defence.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Supabase RLS on everything
&lt;/h3&gt;

&lt;p&gt;Row Level Security enforced at database level — users can only ever read their own rows. If my application code has a bug, the database still protects user data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Took Longer Than Expected
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub App vs OAuth App&lt;/strong&gt;: Spent half a day figuring out which one to use. (Answer: GitHub App for finer permissions and webhooks.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email deliverability&lt;/strong&gt;: Magic links going to spam initially. Fixed by setting up custom SMTP through Resend with proper SPF/DKIM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wildcard DNS for subdomains&lt;/strong&gt;: Hosted public changelog pages at &lt;code&gt;{user}.releasely.io&lt;/code&gt; required wildcard DNS config on Vercel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Was Faster Than Expected
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase auth flow&lt;/strong&gt;: Magic link working in 30 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lemon Squeezy webhooks&lt;/strong&gt;: 2 hours from signup to live payments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude prompt tuning&lt;/strong&gt;: 3 tones working well after maybe 5 iterations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Railway deployment&lt;/strong&gt;: Push to git, it deploys. No configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons for Indie SaaS Builders
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Research competitors thoroughly before naming your product.&lt;/strong&gt; I named mine "ChangelogAI" only to find changelogai.dev existed. Had to rebrand mid-build to Releasely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI in the middle, integrations as the moat.&lt;/strong&gt; Anyone can wrap Claude in a chat interface. Real defensibility comes from GitHub OAuth, Slack bots, Jira sync — places ChatGPT can't go.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ship before perfecting.&lt;/strong&gt; I have things I want to improve. Doesn't matter. Live and getting feedback beats polished and shelved every single time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The studio model compounds.&lt;/strong&gt; Releasely is product #3 in my Devcraft studio (UI kit + dashboard kit being the others). Each product makes the next easier to launch.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://releasely.io" rel="noopener noreferrer"&gt;Releasely&lt;/a&gt; — free for 5 changelogs/month, $9/mo for unlimited.&lt;/p&gt;

&lt;p&gt;Honest feedback welcome. If you ship code and hate writing release notes, this is for you.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>ai</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Angular 17 + Claude API: Build a Streaming AI Dashboard (Starter Kit Inside)</title>
      <dc:creator>Harry</dc:creator>
      <pubDate>Mon, 25 May 2026 15:34:52 +0000</pubDate>
      <link>https://dev.to/harrypt4/angular-17-claude-api-build-a-streaming-ai-dashboard-starter-kit-inside-2nde</link>
      <guid>https://dev.to/harrypt4/angular-17-claude-api-build-a-streaming-ai-dashboard-starter-kit-inside-2nde</guid>
      <description>&lt;p&gt;Every time I start a new dashboard project, I'm rebuilding the same boilerplate — sidebar nav, KPI cards, charts, and then the real time sink: wiring up an AI assistant properly.&lt;/p&gt;

&lt;p&gt;So I packaged it all into a starter kit. Here's what's inside and how the streaming piece works.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in the box
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Frontend — Angular 17 standalone components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dark-themed dashboard shell with sidebar navigation&lt;/li&gt;
&lt;li&gt;4 KPI metric cards (Total Users, Active Users, Revenue, Conversion)&lt;/li&gt;
&lt;li&gt;Revenue bar chart, Category doughnut, User Growth line chart (Chart.js 4)&lt;/li&gt;
&lt;li&gt;Full AI chat panel with streaming responses, suggestion chips, typing cursor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend — Node.js / Express&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude API proxy — your API key never hits the browser&lt;/li&gt;
&lt;li&gt;Server-Sent Events (SSE) so tokens stream word-by-word in real time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/metrics&lt;/code&gt; and &lt;code&gt;/api/chart-data&lt;/code&gt; endpoints (swap for your real DB in minutes)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The streaming part (how it actually works)
&lt;/h2&gt;

&lt;p&gt;This is the bit most tutorials skip over. Here's the SSE endpoint on the backend:&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/chat/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keep-alive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude-sonnet-4-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &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;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;stream&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;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content_block_delta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data: [DONE]&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&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;On the Angular side, the chat service reads the stream with &lt;code&gt;EventSource&lt;/code&gt; and appends each token to the message in real time — so the user sees words appearing as Claude generates them, just like ChatGPT.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get it running in under 10 minutes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install everything&lt;/span&gt;
npm run &lt;span class="nb"&gt;install&lt;/span&gt;:all

&lt;span class="c"&gt;# 2. Add your Claude API key&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;backend/.env.example backend/.env
&lt;span class="c"&gt;# Edit .env → paste ANTHROPIC_API_KEY=sk-ant-...&lt;/span&gt;

&lt;span class="c"&gt;# 3. Start both servers&lt;/span&gt;
npm run dev:backend    &lt;span class="c"&gt;# terminal 1  → http://localhost:3000&lt;/span&gt;
npm run dev:frontend   &lt;span class="c"&gt;# terminal 2  → http://localhost:4200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No Webpack config rabbit holes, no NgModules boilerplate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech stack at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Angular 17 (standalone, no NgModules)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI&lt;/td&gt;
&lt;td&gt;Anthropic Claude API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Charts&lt;/td&gt;
&lt;td&gt;Chart.js 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Node.js + Express&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;SCSS + CSS custom properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming&lt;/td&gt;
&lt;td&gt;Server-Sent Events (SSE)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Customisation takes minutes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Theme&lt;/strong&gt;: edit 3 CSS variables in &lt;code&gt;styles.scss&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mock data → real DB&lt;/strong&gt;: update 2 endpoints in &lt;code&gt;server.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI persona&lt;/strong&gt;: change one &lt;code&gt;systemPrompt&lt;/code&gt; string&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New charts&lt;/strong&gt;: one Chart.js call in the dashboard component&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where to get it
&lt;/h2&gt;

&lt;p&gt;The full source (frontend + backend + README) is on Gumroad: &lt;strong&gt;$59&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://al14ever.gumroad.com/l/gslrqr" rel="noopener noreferrer"&gt;https://al14ever.gumroad.com/l/gslrqr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer questions in the comments — especially if you're integrating Claude into an existing Angular app.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
