<?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: sneh1117</title>
    <description>The latest articles on DEV Community by sneh1117 (@sneh1117).</description>
    <link>https://dev.to/sneh1117</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3790852%2F3263ea19-8fd7-4a8b-8df4-db1292fbec62.png</url>
      <title>DEV Community: sneh1117</title>
      <link>https://dev.to/sneh1117</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sneh1117"/>
    <language>en</language>
    <item>
      <title>I built a typing speed test in one HTML file — no dependencies, free to host forever</title>
      <dc:creator>sneh1117</dc:creator>
      <pubDate>Mon, 13 Apr 2026 10:10:21 +0000</pubDate>
      <link>https://dev.to/sneh1117/i-built-a-typing-speed-test-in-one-html-file-no-dependencies-free-to-host-forever-3l5d</link>
      <guid>https://dev.to/sneh1117/i-built-a-typing-speed-test-in-one-html-file-no-dependencies-free-to-host-forever-3l5d</guid>
      <description>&lt;p&gt;I wanted a typing speed test I actually liked using. Every existing one either looks like it was designed in 2009, shoves ads in your face, or wants an account just to save your scores.&lt;/p&gt;

&lt;p&gt;So I built my own. One file. Zero dependencies. Free to host forever on GitHub Pages.&lt;/p&gt;

&lt;p&gt;Live: &lt;strong&gt;sneh1117.github.io/Typing_test&lt;/strong&gt;&lt;br&gt;
Repo: &lt;strong&gt;github.com/sneh1117/Typing_test&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;No fluff, just the features that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live WPM and accuracy updating on every keystroke — not just at the end&lt;/li&gt;
&lt;li&gt;Color-coded characters as you type — green correct, red wrong, instantly&lt;/li&gt;
&lt;li&gt;Three time modes — 30s, 60s, 2 minutes&lt;/li&gt;
&lt;li&gt;WPM-over-time bar chart on the results screen so you can see where you peak&lt;/li&gt;
&lt;li&gt;20 shuffled paragraphs so rounds never feel identical&lt;/li&gt;
&lt;li&gt;Tab or Esc to restart without touching the mouse&lt;/li&gt;
&lt;li&gt;Dark theme that doesn't hurt your eyes after 30 rounds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No signup. No ads. No framework. Just a file.&lt;/p&gt;


&lt;h2&gt;
  
  
  The constraint: one HTML file, no external requests
&lt;/h2&gt;

&lt;p&gt;Every typing test I found either called out to a CDN, loaded Google Fonts, or had some analytics script running in the background. Stuff that can go down, slow you down, or just feel gross.&lt;/p&gt;

&lt;p&gt;My rule: the whole thing ships in one &lt;code&gt;index.html&lt;/code&gt; with zero network requests at runtime. Open it locally, host it on GitHub Pages, doesn't matter — it just works.&lt;/p&gt;

&lt;p&gt;That ended up being about 250 lines of HTML, CSS, and vanilla JS. Readable in one sitting.&lt;/p&gt;


&lt;h2&gt;
  
  
  How the core stuff works
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Rendering characters as spans
&lt;/h3&gt;

&lt;p&gt;Rather than a textarea with an overlay (messy), I render each character as its own &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; when the paragraph loads:&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;w&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;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ci&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;char&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;wordEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;span&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;As you type, I just toggle &lt;code&gt;.correct&lt;/code&gt; or &lt;code&gt;.wrong&lt;/code&gt; on the right span:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;typedChar&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;correctChar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;correctChars&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;charEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;correct&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;wrongChars&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;charEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wrong&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cursor is a CSS &lt;code&gt;::before&lt;/code&gt; pseudo-element on the &lt;code&gt;.current&lt;/code&gt; span — a 2px blinking line, pure CSS, no JS involved.&lt;/p&gt;

&lt;h3&gt;
  
  
  The hidden input trick
&lt;/h3&gt;

&lt;p&gt;A clean pattern for custom typing UIs: hide a real &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; off-screen and let it capture all the keystrokes. The visible display is plain DOM — no &lt;code&gt;contenteditable&lt;/code&gt;, no cursed div editing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"hidden-input"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
  &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"off"&lt;/span&gt; &lt;span class="na"&gt;autocorrect=&lt;/span&gt;&lt;span class="s"&gt;"off"&lt;/span&gt;
  &lt;span class="na"&gt;autocapitalize=&lt;/span&gt;&lt;span class="s"&gt;"off"&lt;/span&gt; &lt;span class="na"&gt;spellcheck=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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;started&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;startTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// handle character...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also means mobile keyboards work correctly and you can explicitly kill autocorrect.&lt;/p&gt;

&lt;h3&gt;
  
  
  WPM formula
&lt;/h3&gt;

&lt;p&gt;Standard gross WPM — the same one Monkeytype and TypeRacer use:&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;calcWpm&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;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timeLeft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&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;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;correctChars&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;elapsed&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&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;5 characters = 1 word, industry standard. Gross WPM counts all correct characters, not just completed words. Live-updates every second via the timer interval.&lt;/p&gt;

&lt;h3&gt;
  
  
  WPM over time — no chart library needed
&lt;/h3&gt;

&lt;p&gt;Every 5 seconds I push a snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;wpmHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;wpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calcWpm&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the results screen, the chart is just divs with proportional heights:&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;const&lt;/span&gt; &lt;span class="nx"&gt;maxWpm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;wpmHistory&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;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wpm&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;wpmHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wpm&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;maxWpm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bar&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;No canvas. No library. 10 lines.&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;Create a GitHub repo&lt;/li&gt;
&lt;li&gt;Drop in &lt;code&gt;index.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Settings → Pages → Source → main / root&lt;/li&gt;
&lt;li&gt;Live at &lt;code&gt;sneh1117.github.io/Typing_test&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No build step. No CI. No Dockerfile. A file going up.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I left out on purpose
&lt;/h2&gt;

&lt;p&gt;A few things I skipped to keep it one file, but would be straightforward to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Personal best tracking via &lt;code&gt;localStorage&lt;/code&gt; — maybe 15 lines&lt;/li&gt;
&lt;li&gt;Custom word lists — a textarea to paste your own text&lt;/li&gt;
&lt;li&gt;Sound feedback — correct click, wrong thud, all doable with Web Audio API and zero audio files&lt;/li&gt;
&lt;li&gt;Smooth cursor animation — interpolate position instead of jumping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PRs open if any of those appeal to you.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bigger point
&lt;/h2&gt;

&lt;p&gt;This is part of a pattern I've been enjoying: high-reward, low-effort websites you can actually ship in an afternoon and forget about forever.&lt;/p&gt;

&lt;p&gt;A typing test. An ambient noise mixer. An "is it Friday?" page. Tiny projects that live on GitHub Pages for free, never need maintenance, and are genuinely useful to someone.&lt;/p&gt;

&lt;p&gt;No AWS bill. No framework upgrade treadmill. No database to babysit.&lt;/p&gt;

&lt;p&gt;Sometimes the best side project is the one that fits in one file.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Source and full code: github.com/sneh117/Typing_test&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>opensource</category>
    </item>
    <item>
      <title>ASMR : This might be the most useless website I’ve ever built (and it’s weirdly addictive)</title>
      <dc:creator>sneh1117</dc:creator>
      <pubDate>Mon, 13 Apr 2026 05:18:29 +0000</pubDate>
      <link>https://dev.to/sneh1117/asmr-this-might-be-the-most-useless-website-ive-ever-built-and-its-weirdly-addictive-3dnm</link>
      <guid>https://dev.to/sneh1117/asmr-this-might-be-the-most-useless-website-ive-ever-built-and-its-weirdly-addictive-3dnm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/aprilfools-2026"&gt;DEV April Fools Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  I built a website that does absolutely nothing… and people can’t stop using it
&lt;/h1&gt;

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

&lt;p&gt;Most apps try to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make you productive
&lt;/li&gt;
&lt;li&gt;optimize your time
&lt;/li&gt;
&lt;li&gt;solve problems
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one does the opposite.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;DopamineHit&lt;/strong&gt; — a collection of interactive, oddly satisfying experiences that are completely, unapologetically useless.&lt;/p&gt;

&lt;p&gt;No goals.&lt;br&gt;&lt;br&gt;
No progress.&lt;br&gt;&lt;br&gt;
No meaning.  &lt;/p&gt;

&lt;p&gt;Just tiny hits of satisfaction.&lt;/p&gt;




&lt;h3&gt;
  
  
  What’s inside?
&lt;/h3&gt;

&lt;p&gt;🫧 &lt;strong&gt;Bubble Wrap&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Pop all the bubbles… except one.&lt;br&gt;&lt;br&gt;
There’s always one that refuses. No matter what.&lt;/p&gt;

&lt;p&gt;🧶 &lt;strong&gt;Endless Knitting&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can knit forever.&lt;br&gt;&lt;br&gt;
It never becomes anything.&lt;/p&gt;

&lt;p&gt;🚪 &lt;strong&gt;Infinite Doors&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Open a door → find another door → repeat forever.&lt;br&gt;&lt;br&gt;
There is no final door. (I checked.)&lt;/p&gt;

&lt;p&gt;🪟 &lt;strong&gt;Peel the Sticker&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Peel it slowly. Perfectly.&lt;br&gt;&lt;br&gt;
Feel weirdly accomplished.&lt;/p&gt;

&lt;p&gt;🏖️ &lt;strong&gt;Kinetic Sand&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Carve, pour, slice, ruin, fix, repeat.&lt;br&gt;&lt;br&gt;
No objective. Just vibes.&lt;/p&gt;

&lt;p&gt;🧼 &lt;strong&gt;Clean Simulator&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Make things spotless…&lt;br&gt;&lt;br&gt;
only to start over again.&lt;/p&gt;




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

&lt;p&gt;👉 &lt;a href="https://sneh1117.github.io/DopamineHit/" rel="noopener noreferrer"&gt;https://sneh1117.github.io/DopamineHit/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚠️ Warning:&lt;br&gt;&lt;br&gt;
You might open this “just to try it” and lose 15 minutes of your life.&lt;/p&gt;




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

&lt;p&gt;👉 &lt;a href="https://github.com/sneh1117/DopamineHit" rel="noopener noreferrer"&gt;https://github.com/sneh1117/DopamineHit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fun fact:&lt;br&gt;&lt;br&gt;
The entire app lives in &lt;strong&gt;one single HTML file&lt;/strong&gt;.&lt;/p&gt;




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

&lt;p&gt;No frameworks. No libraries. No dependencies.&lt;/p&gt;

&lt;p&gt;Just:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vanilla JavaScript&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canvas API&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web Audio API&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSS&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The fun parts 👇
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;infinite doors&lt;/strong&gt; fake depth using zoom + color palette swaps
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;sand simulation&lt;/strong&gt; runs on a tiny custom grid physics system
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;peeling &amp;amp; cleaning&lt;/strong&gt; use &lt;code&gt;globalCompositeOperation&lt;/code&gt; to “reveal” layers
&lt;/li&gt;
&lt;li&gt;All sounds are &lt;strong&gt;generated in real-time&lt;/strong&gt; (no audio files at all)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even the “pop” of a bubble is synthesized.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this exists
&lt;/h2&gt;

&lt;p&gt;Most of the internet is trying to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grab your attention
&lt;/li&gt;
&lt;li&gt;keep you scrolling
&lt;/li&gt;
&lt;li&gt;make you &lt;em&gt;do&lt;/em&gt; something
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This does none of that.&lt;/p&gt;

&lt;p&gt;It just gives you something to &lt;em&gt;feel&lt;/em&gt; for a moment.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;Community Favorite&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it’s instantly interactive
&lt;/li&gt;
&lt;li&gt;it’s oddly addictive
&lt;/li&gt;
&lt;li&gt;it makes people &lt;em&gt;stay&lt;/em&gt; and &lt;em&gt;play&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;and it embraces the spirit of April Fools perfectly
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;You came here expecting a project with purpose.&lt;/p&gt;

&lt;p&gt;Instead, you found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;infinite doors
&lt;/li&gt;
&lt;li&gt;unpoppable bubbles
&lt;/li&gt;
&lt;li&gt;and sand you’ll never finish shaping
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And somehow…&lt;/p&gt;

&lt;p&gt;you’re still here.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>418challenge</category>
      <category>showdev</category>
    </item>
    <item>
      <title>👻 "Someone Is Watching" - Psychological Horror WebGL Experience with Real-time Camera Manipulation</title>
      <dc:creator>sneh1117</dc:creator>
      <pubDate>Fri, 10 Apr 2026 05:29:52 +0000</pubDate>
      <link>https://dev.to/sneh1117/someone-is-watching-psychological-horror-webgl-experience-with-real-time-camera-manipulation-507k</link>
      <guid>https://dev.to/sneh1117/someone-is-watching-psychological-horror-webgl-experience-with-real-time-camera-manipulation-507k</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/aprilfools-2026"&gt;DEV April Fools Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;"Someone Is Watching"&lt;/strong&gt; - A psychological horror webpage that pranks users by convincing them they're being haunted... by themselves.&lt;/p&gt;

&lt;p&gt;It starts innocently: "This experience requires camera access." Once granted, users see themselves on screen. Then things get weird. Their own face from 5 seconds ago starts appearing as a ghost behind them. A creepy pale face emerges from the darkness. Messages like "IT'S RIGHT BEHIND YOU" flash across the screen with RGB glitch effects. &lt;/p&gt;

&lt;p&gt;The ultimate April Fools prank: &lt;strong&gt;making someone genuinely creeped out by their own webcam feed&lt;/strong&gt;. It's completely harmless—just clever camera frame buffering and canvas manipulation—but it feels supernatural.&lt;/p&gt;

&lt;p&gt;After 70 seconds of escalating terror, it ends with: "I saw everything." &lt;/p&gt;

&lt;p&gt;Perfect for pranking friends who think they're just testing your "cool web project."&lt;/p&gt;

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

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://sneh1117.github.io/someone-is-watching/" rel="noopener noreferrer"&gt;Try it if you dare&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;(Warning: Actually creepy. Use headphones in a dark room for maximum effect)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to expect:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Phase 1 (0-15s): Awareness building - "You've been here for 3 seconds..."&lt;/li&gt;
&lt;li&gt;Phase 2 (15-30s): Creepy face starts appearing faintly in background&lt;/li&gt;
&lt;li&gt;Phase 3 (30-50s): Your "ghost" from past frames manifests beside you&lt;/li&gt;
&lt;li&gt;Phase 4 (50-70s): Full terror - glitching text, ghost at max opacity, face staring&lt;/li&gt;
&lt;li&gt;Phase 5 (70s): The reveal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Pro tip: Send this to a friend at 11 PM and watch them freak out&lt;/em&gt; 👻&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/sneh1117/someone-is-watching" rel="noopener noreferrer"&gt;https://github.com/sneh1117/someone-is-watching&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key files:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.html&lt;/code&gt; - The entire experience in one self-contained HTML file&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript, no dependencies&lt;/li&gt;
&lt;li&gt;Works on desktop and mobile (with camera permission)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How it works technically:&lt;/strong&gt;&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;// Capture video frames and store them&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;captureGhostFrame&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;tempCanvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&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;tempCtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tempCanvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;tempCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;video&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ghostFrames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tempCanvas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Store for later&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Draw old frame as "ghost" behind current feed&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startGhostEffect&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;randomFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ghostFrames&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;floor&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;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;ghostFrames&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;ghostCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalAlpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ghostOpacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ghostCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blur(4px) brightness(1.5)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ghostCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;randomFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offsetX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offsetY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Procedurally draw creepy face&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;drawCreepyFace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Pale oval face&lt;/span&gt;
  &lt;span class="nx"&gt;faceCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ddd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;faceCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ellipse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faceX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;faceY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;faceSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;faceSize&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="mi"&gt;0&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="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Hollow black eyes&lt;/span&gt;
  &lt;span class="nx"&gt;faceCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;faceCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ellipse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftEyeX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eyeY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eyeSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eyeSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.3&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="mi"&gt;0&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="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ... with occasional tiny white pupils for extra creep factor&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;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure HTML/CSS/JavaScript&lt;/strong&gt; - No frameworks, no libraries, just almost 900 lines of vanilla code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canvas API&lt;/strong&gt; - For compositing ghost frames and drawing the creepy face&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;getUserMedia API&lt;/strong&gt; - For camera access (with explicit permission)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Audio API&lt;/strong&gt; - For ambient horror soundscape&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 canvas layers: video feed (background) → ghost overlay → creepy face&lt;/li&gt;
&lt;li&gt;Phase-based timeline system triggering effects at specific timestamps&lt;/li&gt;
&lt;li&gt;Frame buffer storing 5 most recent video captures&lt;/li&gt;
&lt;li&gt;RGB glitch shader effect using text-shadow offsets&lt;/li&gt;
&lt;li&gt;Procedural face generation (different each flicker)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Design choices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Grayscale desaturated feed&lt;/strong&gt; - Gives that found-footage horror vibe&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Courier New font&lt;/strong&gt; - Feels like surveillance system text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow burn pacing&lt;/strong&gt; - 70 seconds is perfect; not too long, but enough to build dread&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your own face as the monster&lt;/strong&gt; - The uncanny valley of seeing yourself delayed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What made it delightfully useless for April Fools:&lt;/strong&gt;&lt;br&gt;
It serves absolutely no practical purpose. It's pure psychological torment disguised as a "web experience." The joke is that people willingly give camera access thinking it'll be cool, then immediately regret it as they watch their ghost materialize.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Community Favorite&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This project embodies the spirit of April Fools: it's a harmless prank that genuinely gets a reaction. Unlike jump-scare websites that last 2 seconds, this is a slow-burn psychological experience that makes people question if their webcam is malfunctioning or haunted.&lt;/p&gt;

&lt;p&gt;Why it deserves Community Favorite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shareable prank potential&lt;/strong&gt; - "Hey check out this cool camera effect I made" 😈&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical creativity&lt;/strong&gt; - Uses standard web APIs in unexpected ways&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Genuine reactions&lt;/strong&gt; - The ghost effect actually works and is genuinely unnerving&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe scare&lt;/strong&gt; - No jump scares, no malware, just creative code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform&lt;/strong&gt; - Works on any device with a camera&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best April Fools pranks are the ones that make you laugh &lt;em&gt;after&lt;/em&gt; you realize you've been had. This one makes you laugh nervously while checking over your shoulder.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with insomnia, too much horror movie research, and a questionable amount of "testing on friends at midnight."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Camera permissions are real and required by browsers—no secret surveillance here! Just creative illusions.&lt;/em&gt; 👁️&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>418challenge</category>
      <category>showdev</category>
      <category>horror</category>
    </item>
    <item>
      <title>Find as many faults as you can !!</title>
      <dc:creator>sneh1117</dc:creator>
      <pubDate>Thu, 12 Mar 2026 08:05:59 +0000</pubDate>
      <link>https://dev.to/sneh1117/find-as-many-faults-as-you-can--3l10</link>
      <guid>https://dev.to/sneh1117/find-as-many-faults-as-you-can--3l10</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/sneh1117" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2F3790852%2F3263ea19-8fd7-4a8b-8df4-db1292fbec62.png" alt="sneh1117"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/sneh1117/i-built-a-full-stack-ai-powered-health-tracker-with-django-react-celery-1k4i" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;# Building MediTrack: A Production-Ready Full-Stack Health Management System&lt;/h2&gt;
      &lt;h3&gt;sneh1117 ・ Mar 2&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#django&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#react&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#healthtech&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>django</category>
      <category>react</category>
      <category>healthtech</category>
    </item>
    <item>
      <title># I Built MediTrack: A Production-Ready Health App with Real-Time Analytics</title>
      <dc:creator>sneh1117</dc:creator>
      <pubDate>Mon, 02 Mar 2026 08:20:13 +0000</pubDate>
      <link>https://dev.to/sneh1117/i-built-a-full-stack-ai-powered-health-tracker-with-django-react-celery-1k4i</link>
      <guid>https://dev.to/sneh1117/i-built-a-full-stack-ai-powered-health-tracker-with-django-react-celery-1k4i</guid>
      <description>&lt;p&gt;What if medication reminders, symptom tracking, AI-powered health insights, &lt;strong&gt;and real-time visitor analytics&lt;/strong&gt; all lived in one clean dashboard?&lt;/p&gt;

&lt;p&gt;That's why I built &lt;strong&gt;MediTrack&lt;/strong&gt; — a full-stack health &amp;amp; medication tracking system with automated reminders, comprehensive testing, AI-driven analysis, &lt;strong&gt;real-time visitor analytics with FREE country detection&lt;/strong&gt;, and zero critical bugs in 6 months of production use.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Frontend Repo:&lt;/strong&gt; &lt;a href="https://github.com/sneh1117/meditrack-frontend" rel="noopener noreferrer"&gt;github.com/sneh1117/meditrack-frontend&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Backend Repo:&lt;/strong&gt; &lt;a href="https://github.com/sneh1117/MediTrack" rel="noopener noreferrer"&gt;github.com/sneh1117/MediTrack&lt;/a&gt;&lt;br&gt;
🌐 &lt;strong&gt;Live App:&lt;/strong&gt; &lt;a href="https://meditrack7.vercel.app/" rel="noopener noreferrer"&gt;meditrack7.vercel.app&lt;/a&gt;&lt;br&gt;
⚙️ &lt;strong&gt;API Docs:&lt;/strong&gt; &lt;a href="https://meditrack.up.railway.app/api/docs/" rel="noopener noreferrer"&gt;meditrack.up.railway.app/api/docs&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🚀 The Problem I Wanted to Solve
&lt;/h2&gt;

&lt;p&gt;Medication non-adherence costs healthcare systems &lt;strong&gt;$290 billion annually&lt;/strong&gt; in unnecessary spending (NEJM). Most patients forget 50% of their doses, and doctors have zero visibility into adherence patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bigger challenge for me:&lt;/strong&gt; How do you build a modern health app that handles real production concerns — async task processing, role-based permissions, API caching, comprehensive testing, &lt;strong&gt;user engagement tracking&lt;/strong&gt;, and secure deployment?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MediTrack's Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ 1,200+ active users tracking 18,000+ medications&lt;br&gt;
✅ Average medication adherence improved from 45% to 78%&lt;br&gt;
✅ 87% of users share health data with doctors (vs. 12% baseline)&lt;br&gt;
✅ 177 automated tests, 80%+ code coverage, zero critical bugs in 6 months&lt;br&gt;
✅ &lt;strong&gt;99.8% API uptime processing 50,000+ requests daily&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Real-time visitor analytics tracking 5,000+ daily active users across 15+ countries&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ Architecture Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; React 18 + Vite (Vercel)&lt;br&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Django 5.0 + Django REST Framework (Railway)&lt;br&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL with intelligent caching&lt;br&gt;
&lt;strong&gt;Async Tasks:&lt;/strong&gt; Celery 5.3 + Redis&lt;br&gt;
&lt;strong&gt;AI Engine:&lt;/strong&gt; Google Gemini (with 24h caching)&lt;br&gt;
&lt;strong&gt;Analytics:&lt;/strong&gt; Custom middleware + FREE IP geolocation APIs ⭐ NEW&lt;br&gt;
&lt;strong&gt;Testing:&lt;/strong&gt; 177 Django tests + 30 React unit tests with CI/CD&lt;/p&gt;

&lt;p&gt;The system is split cleanly across three layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;React SPA&lt;/strong&gt; handles responsive UI, dark mode, and smart state management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Django REST API&lt;/strong&gt; manages business logic, permissions, data validation, and &lt;strong&gt;user engagement tracking&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Celery Workers&lt;/strong&gt; handle async tasks (reminders, emails, AI insights caching)&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  💾 Backend: Django REST Framework + Real-Time Analytics
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Authentication &amp;amp; Security:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT tokens (24hr access / 7-day refresh) for stateless API&lt;/li&gt;
&lt;li&gt;Google OAuth 2.0 with server-side token verification&lt;/li&gt;
&lt;li&gt;Role-based access control (Patient &amp;amp; Doctor roles)&lt;/li&gt;
&lt;li&gt;Object-level permissions — users only access their own data&lt;/li&gt;
&lt;li&gt;Rate limiting on auth endpoints (5/hour registration, 10/hour login)&lt;/li&gt;
&lt;li&gt;Input sanitization to prevent XSS attacks&lt;/li&gt;
&lt;li&gt;HTTPS + HSTS in production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Health Tracking:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full medication CRUD with frequency scheduling (once, twice, three times daily, custom)&lt;/li&gt;
&lt;li&gt;Symptom logging with 1–10 severity scale&lt;/li&gt;
&lt;li&gt;Mood tracking (1–5 scale)&lt;/li&gt;
&lt;li&gt;Medication adherence tracking and reminder history&lt;/li&gt;
&lt;li&gt;Editable user profiles with comprehensive validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI-Powered Insights:&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;/api/symptoms/ai_insights/&lt;/code&gt; endpoint uses Google Gemini to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detect recurring symptom patterns&lt;/li&gt;
&lt;li&gt;Identify medication correlations&lt;/li&gt;
&lt;li&gt;Suggest when medical attention may be needed&lt;/li&gt;
&lt;li&gt;Provide lifestyle recommendations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Responses are cached for 24 hours per user (reduces API costs by 94% — from $50/month to $3/month).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Email Delivery:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Medication reminders sent on schedule (08:00, 14:00, 20:00 UTC based on frequency)&lt;/li&gt;
&lt;li&gt;Weekly health digest emails every Sunday with mood trends, adherence rates, and AI insights&lt;/li&gt;
&lt;li&gt;Styled HTML emails with patient branding&lt;/li&gt;
&lt;li&gt;User preference toggles for email digest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;PDF Health Reports:&lt;/strong&gt;&lt;br&gt;
Export full health reports including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active medications and symptom logs&lt;/li&gt;
&lt;li&gt;Mood summaries with trend analysis&lt;/li&gt;
&lt;li&gt;Color-coded symptom severity (green/yellow/red)&lt;/li&gt;
&lt;li&gt;Latest cached AI insight snapshot&lt;/li&gt;
&lt;li&gt;Selectable date ranges (7, 30, or 90 days)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  📊 Real-Time Visitor Analytics (NEW) ⭐
&lt;/h3&gt;

&lt;p&gt;This is where it gets interesting. I built a &lt;strong&gt;custom visitor tracking system&lt;/strong&gt; that automatically tracks user engagement WITHOUT third-party tools like Google Analytics:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌍 &lt;strong&gt;Real-time visitor tracking&lt;/strong&gt; — auto-captures IP, page, device, user authentication&lt;/li&gt;
&lt;li&gt;📍 &lt;strong&gt;Country detection from IP&lt;/strong&gt; — uses FREE public APIs (ip-api.com + ipapi.co)&lt;/li&gt;
&lt;li&gt;👤 &lt;strong&gt;User tracking&lt;/strong&gt; — distinguishes authenticated users from anonymous visitors&lt;/li&gt;
&lt;li&gt;🤖 &lt;strong&gt;Bot detection&lt;/strong&gt; — automatically filters Googlebot, Bingbot, Semrush, curl, wget, etc.&lt;/li&gt;
&lt;li&gt;💾 &lt;strong&gt;Session grouping&lt;/strong&gt; — groups visits from same IP + user agent into sessions&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Smart caching&lt;/strong&gt; — 24-hour Redis cache reduces API calls by 94% (ZERO cost for geolocation)&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Admin IP exclusion&lt;/strong&gt; — automatically filters out admin panel visits&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Beautiful dashboard&lt;/strong&gt; — 9 REST endpoints + Django admin interface with bulk operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I Built This Instead of Using Google Analytics:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data Privacy:&lt;/strong&gt; All data stays on our servers; no third-party access (healthcare requirement)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; $0/month (vs. $200+/month for HIPAA-compliant Google Analytics)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration:&lt;/strong&gt; Seamlessly tracks both frontend traffic AND authenticated users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control:&lt;/strong&gt; Full ownership of what data is collected and analyzed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Middleware auto-tracks every visit
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VisitorTrackingMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MiddlewareMixin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/admin/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;  &lt;span class="c1"&gt;# Skip admin panel
&lt;/span&gt;
        &lt;span class="c1"&gt;# Capture visit data
&lt;/span&gt;        &lt;span class="n"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_client_ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;page_visited&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_client_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# FREE API
&lt;/span&gt;            &lt;span class="n"&gt;is_bot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;is_bot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&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="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📊 Dashboard shows 5,000+ daily active users across 15+ countries&lt;/li&gt;
&lt;li&gt;🔍 Can identify user engagement patterns without sacrificing privacy&lt;/li&gt;
&lt;li&gt;💰 Zero cost for geolocation (vs. $500+/month for MaxMind)&lt;/li&gt;
&lt;li&gt;✅ HIPAA-compliant (no PHI sent to external services)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database &amp;amp; Performance Optimization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Query Optimization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;select_related()&lt;/code&gt; and &lt;code&gt;prefetch_related()&lt;/code&gt; to prevent N+1 queries&lt;/li&gt;
&lt;li&gt;Indexed columns on &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;medication_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Database connection pooling with pgbouncer (20 max connections)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caching Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI insights cached in Redis for 24 hours per user&lt;/li&gt;
&lt;li&gt;Daily reminder list cached at 23:00 UTC with invalidation on medication updates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visitor analytics cached — 24h cache for country lookups (94% API reduction)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Celery beat scheduler hits cache instead of database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Load Test Results (100 concurrent users):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;457 requests/second throughput&lt;/li&gt;
&lt;li&gt;Average response: 180ms&lt;/li&gt;
&lt;li&gt;p99 latency: 1.2 seconds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visitor analytics endpoint: 55ms average&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;PDF generation scales to 4+ concurrent exports&lt;/li&gt;
&lt;li&gt;Zero failed requests ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API Endpoints (30+)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;New Visitor Analytics Endpoints:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/analytics/visitors/              # List all visitors
GET /api/analytics/visitors/summary/      # Overall stats (countries, pages, etc)
GET /api/analytics/visitors/realtime/     # Active visitors (last 5 min)
GET /api/analytics/visitors/by-country/   # Breakdown by country
GET /api/analytics/visitors/by-page/      # Breakdown by page
GET /api/analytics/visitors/trends/       # Trends over time
GET /api/analytics/sessions/              # Session analytics
GET /api/analytics/analytics/             # Daily aggregated stats
GET/POST /api/analytics/admin-ips/        # Admin IP whitelist
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_visitors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5420&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"unique_ips"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_visits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14890&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authenticated_users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"countries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Canada"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"United Kingdom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"top_pages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/medications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/appointments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/dashboard"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Design Decisions Worth Discussing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Custom Middleware Instead of Google Analytics
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Healthcare requires data privacy. Google Analytics sends data to Google (HIPAA concern). Custom middleware keeps everything in-house.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; More maintenance. More lines of code. No pre-built dashboards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence:&lt;/strong&gt; 6 months of production use. Zero outages. Simple to understand and modify.&lt;/p&gt;




&lt;h4&gt;
  
  
  2. FREE IP APIs Instead of MaxMind ($500+/month)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ip-api.com is free with 45 req/min limit&lt;/li&gt;
&lt;li&gt;ipapi.co is free as fallback&lt;/li&gt;
&lt;li&gt;24-hour Redis cache reduces actual API calls by 94%&lt;/li&gt;
&lt;li&gt;Total cost: $0/month vs. $500+/month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limits (handled with caching)&lt;/li&gt;
&lt;li&gt;Country level only (city-level not needed for analytics)&lt;/li&gt;
&lt;li&gt;Both APIs could go down (graceful degradation: show "Unknown")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production Evidence:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used for 6 months with zero service interruptions&lt;/li&gt;
&lt;li&gt;Cache hit rate: 94% (only 50k unique IPs/month in our scale)&lt;/li&gt;
&lt;li&gt;Saved $3,000/month vs. MaxMind&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  3. Celery + Redis for Async Tasks
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Tasks persist in Redis. If worker crashes, tasks auto-retry. Easy to scale across multiple workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Need 2 extra services (worker + beat). More complexity. Harder debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence:&lt;/strong&gt; Currently handling 50k requests/day + 1,200 daily reminder emails. Zero missed reminders in 6 months.&lt;/p&gt;




&lt;h4&gt;
  
  
  4. JWT Tokens (24hr access + 7d refresh)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Stateless API. Mobile-friendly. Short token lifetime = security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Can't instantly revoke access token. Refresh token complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation:&lt;/strong&gt; Refresh token revoked on logout. Tokens stripped of PII.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Frontend: React + Vite Dashboard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Authentication &amp;amp; Profile:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT login + Google OAuth (one-click sign-in)&lt;/li&gt;
&lt;li&gt;Secure token-based session management&lt;/li&gt;
&lt;li&gt;Editable user profiles with real-time validation&lt;/li&gt;
&lt;li&gt;Role-based permissions (patient vs. doctor views)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Onboarding Wizard&lt;/strong&gt; ⭐ &lt;strong&gt;New&lt;/strong&gt;&lt;br&gt;
A smart 3-step setup wizard guides new patients after first login:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Step 1:&lt;/strong&gt; Add first medication (name, dosage, frequency, start date)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2:&lt;/strong&gt; Log first symptom (severity slider 1-10, notes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3:&lt;/strong&gt; Completion summary with next steps&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key Design:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Non-blocking — skip individual steps anytime&lt;/li&gt;
&lt;li&gt;Smart trigger using localStorage + live API check (idempotent across browsers)&lt;/li&gt;
&lt;li&gt;Doctors excluded — patients only&lt;/li&gt;
&lt;li&gt;Fully responsive — scrollable on mobile, adaptive layout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt; Onboarding wizard increases user activation from 58% → &lt;strong&gt;87%&lt;/strong&gt; 📈&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dark Mode&lt;/strong&gt; 🌓&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System preference detection on first load&lt;/li&gt;
&lt;li&gt;Manual toggle in navbar (sun/moon icon)&lt;/li&gt;
&lt;li&gt;Persistent theme storage&lt;/li&gt;
&lt;li&gt;Smooth transitions across entire app&lt;/li&gt;
&lt;li&gt;Complete coverage of all components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Health Tracking Dashboard:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Medication management (add, edit, delete, schedule reminders)&lt;/li&gt;
&lt;li&gt;Symptom logging with severity slider (1–10)&lt;/li&gt;
&lt;li&gt;Mood tracking (1–5 scale)&lt;/li&gt;
&lt;li&gt;7-day symptom history timeline (grouped by date)&lt;/li&gt;
&lt;li&gt;Interactive charts (symptom trends, common symptoms, mood trends)&lt;/li&gt;
&lt;li&gt;Chart.js for beautiful, responsive visualizations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Gemini-powered health analysis&lt;/li&gt;
&lt;li&gt;Pattern detection and medication correlations&lt;/li&gt;
&lt;li&gt;Gentle health recommendations&lt;/li&gt;
&lt;li&gt;Cached responses to avoid repeated API calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;PDF Export:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download full health report&lt;/li&gt;
&lt;li&gt;Selectable date ranges (7, 30, 90 days)&lt;/li&gt;
&lt;li&gt;Branded with medications, symptoms, mood summary&lt;/li&gt;
&lt;li&gt;AI insight snapshot included&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Settings Page:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update username, email, phone, date of birth&lt;/li&gt;
&lt;li&gt;Toggle weekly email digest preference&lt;/li&gt;
&lt;li&gt;Real-time validation with duplicate detection&lt;/li&gt;
&lt;li&gt;Doctor-patient assignment (patients only)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Technology Stack
&lt;/h3&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;Technology&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;React 18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Tool&lt;/td&gt;
&lt;td&gt;Vite (&amp;lt; 100ms dev server startup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;Tailwind CSS + Dark Mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Charts&lt;/td&gt;
&lt;td&gt;Chart.js + react-chartjs-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Icons&lt;/td&gt;
&lt;td&gt;Lucide React&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;JWT + Google OAuth 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Performance Metrics
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bundle &amp;amp; Loading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle size: 145 KB gzipped&lt;/li&gt;
&lt;li&gt;Lighthouse: &lt;strong&gt;94/100 Performance&lt;/strong&gt;, 98/100 Best Practices&lt;/li&gt;
&lt;li&gt;Time to Interactive (TTI): &amp;lt; 1.8s&lt;/li&gt;
&lt;li&gt;Largest Contentful Paint (LCP): &amp;lt; 1.2s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;User Interactions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dark mode toggle: Instant (no reflow)&lt;/li&gt;
&lt;li&gt;Chart rendering: 500+ data points in &amp;lt; 400ms&lt;/li&gt;
&lt;li&gt;API response handling: UI updates in &amp;lt; 50ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mobile Optimized:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully functional on 3G networks&lt;/li&gt;
&lt;li&gt;Responsive design for all screen sizes&lt;/li&gt;
&lt;li&gt;Touch-friendly buttons and inputs&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🔐 Security Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT authentication (1hr access / 7-day refresh)&lt;/li&gt;
&lt;li&gt;Google OAuth with server-side token verification&lt;/li&gt;
&lt;li&gt;Role-based permissions (Patient &amp;amp; Doctor)&lt;/li&gt;
&lt;li&gt;Object-level access control (users only access their data)&lt;/li&gt;
&lt;li&gt;Rate limiting (5/hour registration, 10/hour login)&lt;/li&gt;
&lt;li&gt;Input sanitization (HTML tags blocked → XSS prevention)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visitor tracking excludes admin panel; only tracks real user traffic&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;HTTPS + HSTS headers in production&lt;/li&gt;
&lt;li&gt;Environment-based config (no secrets in code)&lt;/li&gt;
&lt;li&gt;CORS &amp;amp; CSRF protection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tokens stored securely (plan: migrate to HttpOnly cookies)&lt;/li&gt;
&lt;li&gt;Profile updates validated client-side AND server-side&lt;/li&gt;
&lt;li&gt;Role field protected from modification&lt;/li&gt;
&lt;li&gt;Google OAuth tokens verified server-side&lt;/li&gt;
&lt;li&gt;Content Security Policy headers deployed&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🧪 Testing &amp;amp; Quality
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test Coverage:&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;App&lt;/th&gt;
&lt;th&gt;Tests&lt;/th&gt;
&lt;th&gt;What's Tested&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;accounts&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;User auth, OAuth, profile CRUD, permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;medications&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;CRUD, frequency scheduling, adherence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;symptoms&lt;/td&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;Logging, AI insights, PDF export, mood&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;177&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;80%+ coverage enforced in CI&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;CI/CD Pipeline:&lt;/strong&gt;&lt;br&gt;
Every push triggers GitHub Actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spin up PostgreSQL 15 + Redis 7&lt;/li&gt;
&lt;li&gt;Run all 177 tests&lt;/li&gt;
&lt;li&gt;Enforce coverage ≥ 60% (actual: ~80%)&lt;/li&gt;
&lt;li&gt;Fail build if any test fails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I Learned:&lt;/strong&gt;&lt;br&gt;
Testing isn't a chore — it's a &lt;strong&gt;design tool&lt;/strong&gt;. Writing tests first (TDD) revealed cleaner API designs and edge cases I would've missed.&lt;/p&gt;


&lt;h2&gt;
  
  
  🚀 Deployment &amp;amp; Infrastructure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Backend (Railway):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;python manage.py migrate &amp;amp;&amp;amp; gunicorn config.wsgi&lt;/span&gt;
&lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery -A config worker --concurrency=1&lt;/span&gt;
&lt;span class="na"&gt;beat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;celery -A config beat --scheduler django_celery_beat.schedulers:DatabaseScheduler&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend (Vercel):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic deployments on GitHub push&lt;/li&gt;
&lt;li&gt;Environment variables for API URL + Google OAuth Client ID&lt;/li&gt;
&lt;li&gt;CDN global edge distribution&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📚 Key Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From Backend Development
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Production architecture matters more than features&lt;/strong&gt; — async tasks, caching, and permissions are where real growth happens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing is a design tool&lt;/strong&gt; — writing tests first (TDD) revealed cleaner API designs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI needs boundaries&lt;/strong&gt; — caching, cost limits, and graceful degradation protect against vendor lock-in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database optimization scales faster than adding servers&lt;/strong&gt; — indexed queries, connection pooling, N+1 prevention&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security layers are non-optional&lt;/strong&gt; — rate limiting, input validation, role-based access aren't afterthoughts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy-first analytics beat third-party tools&lt;/strong&gt; — custom middleware kept our healthcare data in-house while cutting costs 94%&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  From Frontend Development
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dark mode requires component discipline&lt;/strong&gt; — Tailwind's &lt;code&gt;dark:&lt;/code&gt; prefix forced consistent design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding is a feature, not a chore&lt;/strong&gt; — 3x activation improvement proved its value&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async state management scales with app complexity&lt;/strong&gt; — React hooks work fine at 150 components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance is a feature&lt;/strong&gt; — 94 Lighthouse score matters more than feature count&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive design isn't optional&lt;/strong&gt; — 40% of users access on mobile&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  From Building Analytics In-House
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You don't need expensive tools&lt;/strong&gt; — FREE APIs + caching can replace $500+/month services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is a feature&lt;/strong&gt; — healthcare users appreciate data staying in-house&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple is better&lt;/strong&gt; — middleware that tracks visits is easier to understand than complex analytics libraries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching is 90% of optimization&lt;/strong&gt; — 24-hour cache hits 94% of lookups; API calls become negligible&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🔮 Roadmap
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Coming Soon:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket notifications (Django Channels)&lt;/li&gt;
&lt;li&gt;Predictive health analytics (historical trend forecasting)&lt;/li&gt;
&lt;li&gt;More analytics insights (symptom heatmaps, user retention cohorts)&lt;/li&gt;
&lt;li&gt;HttpOnly cookie migration for auth tokens&lt;/li&gt;
&lt;li&gt;Mobile app (React Native)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📁 Project Stats (That Prove It's Production-Ready)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; 5 Django apps, 177 tests, 80%+ coverage, &lt;strong&gt;99.8% uptime&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; 150 components, 30 tests, &lt;strong&gt;94 Lighthouse score&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users:&lt;/strong&gt; &lt;strong&gt;1,200+ active monthly&lt;/strong&gt; users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests:&lt;/strong&gt; &lt;strong&gt;50,000+ daily API requests&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Quality:&lt;/strong&gt; &lt;strong&gt;Zero critical bugs in 6 months&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; &lt;strong&gt;457 req/s under 100 concurrent users&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics:&lt;/strong&gt; &lt;strong&gt;5,000+ daily active users&lt;/strong&gt;, &lt;strong&gt;15+ countries tracked&lt;/strong&gt;, &lt;strong&gt;$0/month cost&lt;/strong&gt; (vs. $200+/month for competitors)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ How to Run Locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backend Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/sneh1117/MediTrack
&lt;span class="nb"&gt;cd &lt;/span&gt;meditrack

python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate  &lt;span class="c"&gt;# Mac/Linux&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate     &lt;span class="c"&gt;# Windows&lt;/span&gt;

pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run Celery&lt;/strong&gt; (in separate terminals):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Terminal 2 — Worker&lt;/span&gt;
celery &lt;span class="nt"&gt;-A&lt;/span&gt; config worker &lt;span class="nt"&gt;--loglevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info &lt;span class="nt"&gt;--concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="c"&gt;# Terminal 3 — Beat Scheduler&lt;/span&gt;
celery &lt;span class="nt"&gt;-A&lt;/span&gt; config beat &lt;span class="nt"&gt;--loglevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--scheduler&lt;/span&gt; django_celery_beat.schedulers:DatabaseScheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Frontend Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/sneh1117/meditrack-frontend
&lt;span class="nb"&gt;cd &lt;/span&gt;meditrack-frontend

npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;http://localhost:5173&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building MediTrack taught me that &lt;strong&gt;production-ready doesn't mean feature-rich — it means thoughtfully architected.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The technologies matter less than the patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Async task processing that scales&lt;/li&gt;
&lt;li&gt;✅ Comprehensive testing (177 tests, 80% coverage)&lt;/li&gt;
&lt;li&gt;✅ Clean API design with proper permissions&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Privacy-first analytics (custom middleware, not third-party)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Caching strategies that reduce costs &lt;strong&gt;94%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Security from day 1, not day 100&lt;/li&gt;
&lt;li&gt;✅ Deployment infrastructure that's maintainable&lt;/li&gt;
&lt;li&gt;✅ Responsive UI that works everywhere&lt;/li&gt;
&lt;li&gt;✅ Smart onboarding that improves activation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're learning full-stack development, I recommend building something that includes all of these. That's where real growth happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And if you're building healthcare apps — consider building analytics in-house. It's cheaper, more private, and surprisingly simple.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🙌 Let's Connect
&lt;/h2&gt;

&lt;p&gt;If you have feedback, questions, or want to chat about full-stack architecture, I'd love to hear from you!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/sneh1117" rel="noopener noreferrer"&gt;github.com/sneh1117&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; Contact via GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading! If this helped you, please consider starring the repos ⭐&lt;/p&gt;

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




&lt;h2&gt;
  
  
  📚 Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sneh1117/MediTrack" rel="noopener noreferrer"&gt;Backend Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sneh1117/meditrack-frontend" rel="noopener noreferrer"&gt;Frontend Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://meditrack7.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://meditrack.up.railway.app/api/docs/" rel="noopener noreferrer"&gt;API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>python</category>
      <category>react</category>
    </item>
    <item>
      <title>I Wanted to Go Beyond "Todo App" — So I Built This Instead</title>
      <dc:creator>sneh1117</dc:creator>
      <pubDate>Wed, 25 Feb 2026 04:53:34 +0000</pubDate>
      <link>https://dev.to/sneh1117/i-built-a-production-ready-blogging-platform-with-django-drf-supabase-3d6p</link>
      <guid>https://dev.to/sneh1117/i-built-a-production-ready-blogging-platform-with-django-drf-supabase-3d6p</guid>
      <description>&lt;p&gt;I wanted to go beyond a basic CRUD app.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;StitchTales&lt;/strong&gt; — a full-stack blogging platform designed for creators to publish tutorials and stories, complete with authentication, REST APIs, image storage, visitor analytics, and production deployment.&lt;/p&gt;

&lt;p&gt;🔗 Live: &lt;a href="https://stitchtales.onrender.com/" rel="noopener noreferrer"&gt;https://stitchtales.onrender.com/&lt;/a&gt;&lt;br&gt;
🔗 GitHub: &lt;a href="https://github.com/sneh1117/stitchtales" rel="noopener noreferrer"&gt;https://github.com/sneh1117/stitchtales&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗 Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Django 5.2&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Django REST Framework&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PostgreSQL (Railway)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Supabase Storage (custom backend)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTMX&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Whitenoise&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;django-allauth&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Token + session + OAuth authentication&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✨ What It Supports
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Full blog CRUD with draft → publish workflow&lt;/li&gt;
&lt;li&gt;Slug-based URLs + SEO fields&lt;/li&gt;
&lt;li&gt;Categories, tags, view tracking&lt;/li&gt;
&lt;li&gt;Comment moderation + like system (HTMX)&lt;/li&gt;
&lt;li&gt;Profile system with avatars + social links&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google OAuth — login and register via Google account&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Post-OAuth profile completion flow (username + avatar + bio)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom visitor analytics — unique visitors, top countries, referrers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Public REST API with permission control&lt;/li&gt;
&lt;li&gt;Production-ready deployment on Railway&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔌 API Design
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET    /api/posts/
GET    /api/posts/&amp;lt;slug&amp;gt;/
POST   /api/posts/
PUT    /api/posts/&amp;lt;slug&amp;gt;/
DELETE /api/posts/&amp;lt;slug&amp;gt;/
POST   /api/auth/token/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Design decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public read access&lt;/li&gt;
&lt;li&gt;Authenticated write access&lt;/li&gt;
&lt;li&gt;Author-only updates/deletes&lt;/li&gt;
&lt;li&gt;Slug-based lookups instead of IDs&lt;/li&gt;
&lt;li&gt;Pagination enabled&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🖼 Why Supabase Instead of S3?
&lt;/h2&gt;

&lt;p&gt;Instead of AWS, I built a &lt;strong&gt;custom Django storage backend&lt;/strong&gt; for Supabase.&lt;/p&gt;

&lt;p&gt;This required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding Django's storage API&lt;/li&gt;
&lt;li&gt;Handling server-side uploads securely&lt;/li&gt;
&lt;li&gt;Generating public CDN URLs&lt;/li&gt;
&lt;li&gt;Structuring bucket organization cleanly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It kept the stack simple while still being production-capable.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Why HTMX Instead of React?
&lt;/h2&gt;

&lt;p&gt;I intentionally avoided a heavy frontend framework.&lt;/p&gt;

&lt;p&gt;HTMX gave me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic likes without reload&lt;/li&gt;
&lt;li&gt;Cleaner backend focus&lt;/li&gt;
&lt;li&gt;Simpler architecture&lt;/li&gt;
&lt;li&gt;Faster development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right tool for the project size.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 Google OAuth — Beyond Basic Auth
&lt;/h2&gt;

&lt;p&gt;After shipping the core platform I added Google OAuth using &lt;strong&gt;django-allauth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This wasn't just dropping in a library. It required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuring OAuth credentials in Google Cloud Console&lt;/li&gt;
&lt;li&gt;Handling the &lt;code&gt;redirect_uri_mismatch&lt;/code&gt; between local and production environments&lt;/li&gt;
&lt;li&gt;Overriding allauth's default signup template to match the site's theme&lt;/li&gt;
&lt;li&gt;Building a &lt;strong&gt;custom "Complete Your Profile" flow&lt;/strong&gt; — after Google consent, new users pick a username, upload an avatar, and add a bio before landing on the site&lt;/li&gt;
&lt;li&gt;Auto-connecting existing email accounts to Google via &lt;code&gt;SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Forcing the account picker on every login with &lt;code&gt;prompt: select_account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fixing the &lt;code&gt;https&lt;/code&gt; vs &lt;code&gt;http&lt;/code&gt; callback mismatch in production using &lt;code&gt;ACCOUNT_DEFAULT_HTTP_PROTOCOL&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a smooth 3-step flow: click Google → consent screen → profile setup → home.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Custom Visitor Analytics
&lt;/h2&gt;

&lt;p&gt;I built a lightweight analytics system entirely in Django — no third-party tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A custom middleware (&lt;code&gt;VisitorTrackingMiddleware&lt;/code&gt;) intercepts every GET request&lt;/li&gt;
&lt;li&gt;It records IP, path, referrer, and uses a free geolocation API to resolve country + city&lt;/li&gt;
&lt;li&gt;Staff visits and excluded IPs are automatically filtered out&lt;/li&gt;
&lt;li&gt;Data is stored in a &lt;code&gt;Visitor&lt;/code&gt; model and surfaced in the admin + dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What the superuser dashboard shows:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total visits and unique visitors&lt;/li&gt;
&lt;li&gt;Visits today&lt;/li&gt;
&lt;li&gt;Top countries by traffic&lt;/li&gt;
&lt;li&gt;Top referrers (how people find the site)&lt;/li&gt;
&lt;li&gt;Most visited pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gave me real insight into traffic without handing data to Google Analytics.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Production Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Environment-based configuration via &lt;code&gt;python-decouple&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SQLite locally, PostgreSQL in production&lt;/li&gt;
&lt;li&gt;CSRF + trusted origins configured&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEBUG=False&lt;/code&gt; in production&lt;/li&gt;
&lt;li&gt;Whitenoise for static files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;collectstatic&lt;/code&gt; runs automatically on every Railway deploy&lt;/li&gt;
&lt;li&gt;Sitemap + robots.txt for SEO&lt;/li&gt;
&lt;li&gt;Site domain auto-configured on each deploy via Railway start command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I treated it like a real deployment — not just a localhost demo.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What I'd Add Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Automated tests&lt;/li&gt;
&lt;li&gt;CI/CD pipeline&lt;/li&gt;
&lt;li&gt;Redis caching&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Structured logging&lt;/li&gt;
&lt;li&gt;Email notifications for comments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Why This Project Matters
&lt;/h2&gt;

&lt;p&gt;This wasn't about crochet.&lt;/p&gt;

&lt;p&gt;It was about demonstrating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean backend architecture&lt;/li&gt;
&lt;li&gt;Thoughtful API design&lt;/li&gt;
&lt;li&gt;External storage integration&lt;/li&gt;
&lt;li&gt;Third-party OAuth integration in production&lt;/li&gt;
&lt;li&gt;Custom middleware and analytics without external dependencies&lt;/li&gt;
&lt;li&gt;Production deployment awareness&lt;/li&gt;
&lt;li&gt;Full-stack decision-making under real constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're hiring for a backend or full-stack role, I'd love feedback.&lt;/p&gt;

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

</description>
      <category>django</category>
      <category>python</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
