<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Projects on Vijay Kodam</title><link>https://vijay.eu/categories/projects/</link><description>Recent content in Projects on Vijay Kodam</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 04 Jul 2026 10:00:00 +0300</lastBuildDate><atom:link href="https://vijay.eu/categories/projects/index.xml" rel="self" type="application/rss+xml"/><item><title>model-audit Claude Code SKILL</title><link>https://vijay.eu/projects/model-audit/</link><pubDate>Sat, 04 Jul 2026 10:00:00 +0300</pubDate><guid>https://vijay.eu/projects/model-audit/</guid><description>&lt;p&gt;A Claude Code skill, installable as a plugin, that audits which Claude model handled your sessions. Use it to confirm your coding requests ran on the model you selected (Fable 5, Opus 4.8, and so on) and were not silently served by a different one.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vijay.eu/images/projects/model-audit.webp" alt="A Claude Code session that runs /model-audit, next to the model-audit table of sessions and the Claude model that answered each one"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/vijaykodam/model-audit" class="button"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="why"&gt;Why&lt;/h2&gt;
&lt;p&gt;Claude Code&amp;rsquo;s built-in commands do not answer &amp;ldquo;which model produced this response?&amp;rdquo;. &lt;code&gt;/usage&lt;/code&gt; shows plan quota, &lt;code&gt;/cost&lt;/code&gt; shows spend, &lt;code&gt;/context&lt;/code&gt; shows tokens — none report model provenance.&lt;/p&gt;
&lt;p&gt;The ground truth is already on your machine. Claude Code writes every session transcript to &lt;code&gt;~/.claude/projects/&lt;/code&gt;, and every assistant message carries a &lt;code&gt;&amp;quot;model&amp;quot;&lt;/code&gt; field with the exact model ID that generated it. model-audit reads that per-message audit trail and rolls it up per session.&lt;/p&gt;
&lt;h2 id="install"&gt;Install&lt;/h2&gt;
&lt;p&gt;As a plugin (recommended):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/plugin marketplace add vijaykodam/model-audit
/plugin install model-audit@model-audit
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or from your shell, outside a session:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;claude plugin marketplace add vijaykodam/model-audit
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;claude plugin install model-audit@model-audit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run &lt;code&gt;/reload-plugins&lt;/code&gt; (or restart Claude Code) to activate.&lt;/p&gt;
&lt;h2 id="use"&gt;Use&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/model-audit # audit the last 7 days (default)
/model-audit 30 # any number of days
/model-audit all # full session history
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="example-output"&gt;Example output&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Claude Code sessions active in the last 7 day(s)
LAST ACTIVE PROJECT SESSION MODELS (responses)
2026-07-03 09:47 -vijay-projects-project3 85f5d9a6-ec9f-4dc2-... claude-fable-5(24)
2026-07-02 11:18 -vijay-projects-project1 4a347153-4458-47f8-... claude-fable-5(278)
2026-07-02 07:53 -vijay-projects-project1 1a89419d/agent-ab3a2f9e5caae claude-opus-4-8(21)
2026-06-30 18:06 -vijay-projects-project3 a4b9c607-b1e8-4c92-... claude-opus-4-8(128)
Totals across all sessions:
598 claude-fable-5
504 claude-opus-4-8
51 claude-haiku-4-5-20251001
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Model counts are per response, so a session that changed model mid-way shows both models with their proportions. A silent downgrade shows up as an unexpected model count inside a session you expected to be on one model. Subagents appear as separate rows (the &lt;code&gt;SESSION&lt;/code&gt; looks like &lt;code&gt;1a89419d/agent-...&lt;/code&gt;) — they legitimately run on different models but never handle the main conversation turns.&lt;/p&gt;
&lt;h2 id="notes"&gt;Notes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Everything runs locally. It is a small shell script reading files on your own machine; nothing is sent over the internet.&lt;/li&gt;
&lt;li&gt;No dependencies beyond standard tools (bash, find, grep, sed, awk). Works on macOS and Linux.&lt;/li&gt;
&lt;li&gt;Apache-2.0 licensed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For full documentation and the source, see the &lt;a href="https://github.com/vijaykodam/model-audit"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Retro Racer</title><link>https://vijay.eu/projects/retro-racer/</link><pubDate>Wed, 10 Jun 2026 12:00:00 +0300</pubDate><guid>https://vijay.eu/projects/retro-racer/</guid><description>&lt;div class="retro-racer"&gt;
&lt;h1 class="racer-title"&gt;Retro Racer&lt;/h1&gt;
&lt;p class="racer-subtitle"&gt;A 3D arcade racer. One lap, against the clock.&lt;/p&gt;
&lt;div class="racer-frame"&gt;
&lt;canvas id="racerCanvas"&gt;&lt;/canvas&gt;
&lt;div class="racer-banner hidden" id="racerBanner" aria-live="polite"&gt;&lt;/div&gt;
&lt;div class="racer-hud" aria-live="off"&gt;
&lt;div class="hud-item"&gt;
&lt;span class="hud-label"&gt;Time&lt;/span&gt;
&lt;span class="hud-value" id="hudTime"&gt;0:00.0&lt;/span&gt;
&lt;/div&gt;
&lt;div class="hud-item hud-item--speed"&gt;
&lt;span class="hud-label"&gt;Speed&lt;/span&gt;
&lt;span class="hud-value" id="hudSpeed"&gt;0 km/h&lt;/span&gt;
&lt;/div&gt;
&lt;div class="hud-item"&gt;
&lt;span class="hud-label"&gt;Progress&lt;/span&gt;
&lt;span class="hud-value" id="hudProgress"&gt;0%&lt;/span&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="racer-overlay" id="racerStart"&gt;
&lt;h2&gt;Retro Racer&lt;/h2&gt;
&lt;p&gt;Complete one lap as fast as you can. Dodge traffic and stay on the road - grass and crashes will slow you down.&lt;/p&gt;
&lt;p&gt;Arrow keys or WASD to drive. On touch devices, hold gas/brake with your left thumb and steer with your right.&lt;/p&gt;
&lt;button class="racer-btn" id="racerStartBtn" type="button" disabled&gt;Start Race&lt;/button&gt;
&lt;p class="racer-loading" id="racerLoading"&gt;Loading 3D assets...&lt;/p&gt;
&lt;p class="racer-best" id="racerStartBest"&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="racer-overlay hidden" id="racerResults"&gt;
&lt;h2&gt;Finish!&lt;/h2&gt;
&lt;p class="racer-result-time" id="racerResultTime"&gt;&lt;/p&gt;
&lt;p class="racer-best" id="racerResultBest"&gt;&lt;/p&gt;
&lt;button class="racer-btn" id="racerAgainBtn" type="button"&gt;Race Again&lt;/button&gt;
&lt;/div&gt;
&lt;div class="racer-cinematic hidden" id="racerCinematic" aria-hidden="true"&gt;
&lt;div class="racer-letterbox racer-letterbox--top"&gt;&lt;/div&gt;
&lt;div class="racer-letterbox racer-letterbox--bottom"&gt;&lt;/div&gt;
&lt;div class="racer-flyby-label"&gt;
&lt;span class="racer-flyby-name"&gt;Mazda Miata&lt;/span&gt;
&lt;span class="racer-flyby-sub"&gt;MX-5 NA&lt;/span&gt;
&lt;/div&gt;
&lt;button class="racer-skip-btn" id="racerSkipBtn" type="button"&gt;Skip &amp;rsaquo;&amp;rsaquo;&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="racer-touch"&gt;
&lt;div class="touch-cluster touch-cluster--drive" aria-label="Speed controls (left thumb)"&gt;
&lt;button class="touch-btn touch-btn--gas" id="racerBtnGas" type="button" aria-label="Accelerate"&gt;GAS&lt;/button&gt;
&lt;button class="touch-btn touch-btn--brake" id="racerBtnBrake" type="button" aria-label="Brake"&gt;BRAKE&lt;/button&gt;
&lt;/div&gt;
&lt;div class="touch-cluster touch-cluster--steer" aria-label="Steering controls (right thumb)"&gt;
&lt;button class="touch-btn touch-btn--arrow" id="racerBtnLeft" type="button" aria-label="Steer left"&gt;&amp;larr;&lt;/button&gt;
&lt;button class="touch-btn touch-btn--arrow" id="racerBtnRight" type="button" aria-label="Steer right"&gt;&amp;rarr;&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p class="racer-note"&gt;Built with vanilla JavaScript and &lt;a href="https://threejs.org/" rel="noopener"&gt;three.js&lt;/a&gt;. Car models "&lt;a href="https://sketchfab.com/3d-models/mazda-miata-mx-5-na-d51fcd44b74f4daf8012c41e0400c041" rel="noopener"&gt;Mazda Miata MX-5 NA&lt;/a&gt;" and "&lt;a href="https://sketchfab.com/3d-models/toyota-ae86-trueno-fe02fba6302e450ea8424591493341ea" rel="noopener"&gt;Toyota AE86 Trueno&lt;/a&gt;" by Lexyc16 on Sketchfab (CC-BY 4.0). Textures and lighting from &lt;a href="https://polyhaven.com/" rel="noopener"&gt;Poly Haven&lt;/a&gt; (CC0). Your best lap time is stored locally in your browser.&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>Tesla Wrap Maker</title><link>https://vijay.eu/projects/tesla-wrap/</link><pubDate>Sat, 09 May 2026 00:00:00 +0300</pubDate><guid>https://vijay.eu/projects/tesla-wrap/</guid><description>&lt;p&gt;Design custom Tesla wraps in the browser, download the PNG for Paint Shop, and preview supported models in 3D.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://vijay.eu/tesla-wrap/" class="button"&gt;Open Tesla Wrap Maker&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vijay.eu/images/tesla_wrap_screenshot.png" alt="Tesla Wrap Maker screenshot"&gt;&lt;/p&gt;</description></item><item><title>OpenClaw Skill for Finland's Helmet Library</title><link>https://vijay.eu/projects/helmet/</link><pubDate>Sun, 12 Apr 2026 10:00:00 +0300</pubDate><guid>https://vijay.eu/projects/helmet/</guid><description>&lt;p&gt;An OpenClaw SKILL for the Finnish public library Helmet (Helsinki Metropolitan Area libraries). With it, you can ask your OpenClaw agent from WhatsApp: &amp;ldquo;Is anything overdue for the kids at the library?&amp;rdquo; or &amp;ldquo;Place a hold on AI Engineering by Chip Huyen&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vijay.eu/images/projects/helmet.webp" alt="A WhatsApp chat with the OpenClaw agent about overdue library loans, next to the Helmet CLI commands that answer it"&gt;&lt;/p&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/9fbVeMAD6SA?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="Demo of the Helmet OpenClaw skill"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="why"&gt;Why&lt;/h2&gt;
&lt;p&gt;I had recently set up OpenClaw and wanted it to simplify my daily life. My kids and I read a lot of books, and we borrow them from Helmet.&lt;/p&gt;
&lt;p&gt;Helmet had no CLI or API to manage your library account. It has an open API to search books, but nothing for loans, holds, or fines. So I built the Helmet CLI first, and then a SKILL so that my OpenClaw agent can use it.&lt;/p&gt;
&lt;h2 id="what-the-cli-does"&gt;What the CLI does&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Check loans, and renew one item or all renewable items.&lt;/li&gt;
&lt;li&gt;List, place, and cancel holds, with pickup deadline and queue position.&lt;/li&gt;
&lt;li&gt;Show fines and a full account summary.&lt;/li&gt;
&lt;li&gt;Search the Helmet catalog without a login.&lt;/li&gt;
&lt;li&gt;Keep several library cards as profiles, so one command can cover the whole family.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every command accepts &lt;code&gt;--json&lt;/code&gt;, which makes the output easy for an agent to read.&lt;/p&gt;
&lt;h2 id="install"&gt;Install&lt;/h2&gt;
&lt;p&gt;Install the CLI from npm, then save a library card:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install -g @helmet-ai/helmet
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helmet login
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install the SKILL from &lt;a href="https://clawhub.ai/vijaykodam/helmet"&gt;ClawHub&lt;/a&gt;, or with the &lt;code&gt;skills&lt;/code&gt; CLI:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx skills add vijaykodam/helmet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://github.com/vijaykodam/helmet" class="button"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;</description></item><item><title>url-logger Claude Code Plugin</title><link>https://vijay.eu/projects/url-logger/</link><pubDate>Mon, 09 Mar 2026 10:00:00 +0200</pubDate><guid>https://vijay.eu/projects/url-logger/</guid><description>&lt;p&gt;A Claude Code plugin that logs all &lt;code&gt;WebSearch&lt;/code&gt; and &lt;code&gt;WebFetch&lt;/code&gt; URLs to &lt;code&gt;.claude/web-access.log&lt;/code&gt;, with session IDs and timestamps.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://vijay.eu/images/projects/url-logger.webp" alt="A Claude Code session that searches and fetches web pages, next to a terminal that shows the same URLs in the url-logger log file"&gt;&lt;/p&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/uGmm0YHIa2E?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="Demo of the url-logger Claude Code plugin"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="why"&gt;Why&lt;/h2&gt;
&lt;p&gt;When you do research or ask Claude Code CLI to search the Internet, it opens many websites. I wanted a way to log all those URLs in a log file.&lt;/p&gt;
&lt;p&gt;Claude Code has no setting for this. Claude stores session history in JSONL files, which can contain URLs. But those URLs are mixed with a lot of other information, and they are spread across several session files.&lt;/p&gt;
&lt;p&gt;The next option was a hook. But Claude Code&amp;rsquo;s &lt;code&gt;/hookify&lt;/code&gt; cannot write log files; it can only show a warning in the Claude session. This was the perfect use case to build my own hook and publish it as a plugin.&lt;/p&gt;
&lt;h2 id="install"&gt;Install&lt;/h2&gt;
&lt;p&gt;Run these two commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Add the marketplace (one-time)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;claude plugin marketplace add vijaykodam/url-logger
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Install the plugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;claude plugin install url-logger
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restart Claude Code CLI after the install. The plugin starts to work only after the restart.&lt;/p&gt;
&lt;h2 id="log-format"&gt;Log format&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[2026-03-07T14:23:01Z] session=abc123 tool=WebSearch query=&amp;#34;claude code plugins&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[2026-03-07T14:23:05Z] session=abc123 tool=WebFetch url=&amp;#34;https://example.com/docs&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, the plugin writes the log to &lt;code&gt;.claude/web-access.log&lt;/code&gt; in your working directory. To use one log for all projects, set &lt;code&gt;URL_LOGGER_PATH&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export URL_LOGGER_PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;~/.claude/web-access.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The plugin uses a Claude Code &lt;code&gt;PreToolUse&lt;/code&gt; hook. It works only in Claude Code CLI, not in Claude.ai or the Claude Desktop app.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/vijaykodam/url-logger" class="button"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Note Quest</title><link>https://vijay.eu/projects/note-quest/</link><pubDate>Thu, 26 Feb 2026 00:00:00 +0200</pubDate><guid>https://vijay.eu/projects/note-quest/</guid><description>&lt;p&gt;Learn to read music notes on the grand staff! Pick the correct note name for each question.&lt;/p&gt;
&lt;p&gt;Created using Claude Code CLI on 26th Feb 2026. V2 released on 8th Mar 2026.
&lt;link rel="stylesheet" href="https://vijay.eu/css/note-quest.css"&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/vexflow/build/cjs/vexflow.js"&gt;&lt;/script&gt;
&lt;div class="note-quest-wrapper"&gt;
&lt;div class="nq-screen start-screen active" id="nqStartScreen"&gt;
&lt;label class="nq-helper-toggle"&gt;
&lt;input type="checkbox" id="nqShowNoteNames"&gt; Show note names on staff
&lt;small class="nq-helper-hint"&gt;Auto for Levels 1–2, 4 · Ignored for 8–10&lt;/small&gt;
&lt;/label&gt;
&lt;div class="level-grid"&gt;
&lt;div class="level-row"&gt;
&lt;span class="row-label easy"&gt;Easy&lt;/span&gt;
&lt;div class="level-boxes"&gt;
&lt;button class="level-box easy" data-level="1"&gt;
&lt;span class="level-num"&gt;1&lt;/span&gt;
&lt;span class="level-detail"&gt;First Notes · Both clefs · F3,C4 / C4,G4&lt;/span&gt;
&lt;/button&gt;
&lt;button class="level-box easy" data-level="2"&gt;
&lt;span class="level-num"&gt;2&lt;/span&gt;
&lt;span class="level-detail"&gt;Staff Basics · Both clefs · F3–G4&lt;/span&gt;
&lt;/button&gt;
&lt;button class="level-box easy" data-level="3"&gt;
&lt;span class="level-num"&gt;3&lt;/span&gt;
&lt;span class="level-detail"&gt;Getting Comfortable · Both clefs · C3–C5&lt;/span&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="level-row"&gt;
&lt;span class="row-label medium"&gt;Medium&lt;/span&gt;
&lt;div class="level-boxes"&gt;
&lt;button class="level-box medium" data-level="4"&gt;
&lt;span class="level-num"&gt;4&lt;/span&gt;
&lt;span class="level-detail"&gt;Wider Range · Both clefs · A2–E5&lt;/span&gt;
&lt;/button&gt;
&lt;button class="level-box medium" data-level="5"&gt;
&lt;span class="level-num"&gt;5&lt;/span&gt;
&lt;span class="level-detail"&gt;Grand Staff · Both clefs · C2–C6&lt;/span&gt;
&lt;/button&gt;
&lt;button class="level-box medium" data-level="6"&gt;
&lt;span class="level-num"&gt;6&lt;/span&gt;
&lt;span class="level-detail"&gt;Expanding Range · Both clefs · A1–E6&lt;/span&gt;
&lt;/button&gt;
&lt;button class="level-box medium" data-level="7"&gt;
&lt;span class="level-num"&gt;7&lt;/span&gt;
&lt;span class="level-detail"&gt;Full Staff · Both clefs · E2–C6&lt;/span&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="level-row"&gt;
&lt;span class="row-label hard"&gt;Hard&lt;/span&gt;
&lt;div class="level-boxes"&gt;
&lt;button class="level-box hard" data-level="8"&gt;
&lt;span class="level-num"&gt;8&lt;/span&gt;
&lt;span class="level-detail"&gt;Ledger Lines · Both clefs · C2–E6&lt;/span&gt;
&lt;/button&gt;
&lt;button class="level-box hard" data-level="9"&gt;
&lt;span class="level-num"&gt;9&lt;/span&gt;
&lt;span class="level-detail"&gt;Extended Range · Both clefs · B1–E6&lt;/span&gt;
&lt;/button&gt;
&lt;button class="level-box hard" data-level="10"&gt;
&lt;span class="level-num"&gt;10&lt;/span&gt;
&lt;span class="level-detail"&gt;Note Master · Both clefs · A1–E6&lt;/span&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="nq-version"&gt;v2.1&lt;/div&gt;
&lt;/div&gt;
&lt;div class="nq-screen" id="nqGameScreen"&gt;
&lt;div class="nq-header"&gt;
&lt;div class="nq-logo"&gt;Note Quest&lt;/div&gt;
&lt;div class="nq-progress" id="nqProgress"&gt;1 / 10&lt;/div&gt;
&lt;/div&gt;
&lt;div class="nq-container"&gt;
&lt;div class="staff-card" id="nqStaffCard"&gt;&lt;/div&gt;
&lt;div class="choices" id="nqChoices"&gt;
&lt;button class="choice-btn" data-note="C"&gt;C&lt;/button&gt;
&lt;button class="choice-btn" data-note="D"&gt;D&lt;/button&gt;
&lt;button class="choice-btn" data-note="E"&gt;E&lt;/button&gt;
&lt;button class="choice-btn" data-note="F"&gt;F&lt;/button&gt;
&lt;button class="choice-btn" data-note="G"&gt;G&lt;/button&gt;
&lt;button class="choice-btn" data-note="A"&gt;A&lt;/button&gt;
&lt;button class="choice-btn" data-note="B"&gt;B&lt;/button&gt;
&lt;/div&gt;
&lt;div class="stats-bar"&gt;
&lt;span&gt;Streak: &lt;strong id="nqStreakDisplay"&gt;0&lt;/strong&gt;&lt;/span&gt;
&lt;span&gt;Score: &lt;strong id="nqScoreDisplay"&gt;0&lt;/strong&gt;&lt;/span&gt;
&lt;span id="nqTimerDisplay"&gt;0:00&lt;/span&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="nq-screen results-screen" id="nqResultsScreen"&gt;
&lt;h2&gt;Round Complete!&lt;/h2&gt;
&lt;div class="stars" id="nqStarsDisplay"&gt;&lt;/div&gt;
&lt;div class="results-stats" id="nqResultsStats"&gt;&lt;/div&gt;
&lt;button class="play-again-btn"&gt;Play Again&lt;/button&gt;
&lt;/div&gt;
&lt;canvas id="nqConfetti"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;script src="https://vijay.eu/js/note-quest.js"&gt;&lt;/script&gt;
&lt;/p&gt;</description></item><item><title>Tic Tac Toe</title><link>https://vijay.eu/projects/tic-tac-toe/</link><pubDate>Wed, 02 Jul 2025 15:20:07 +0300</pubDate><guid>https://vijay.eu/projects/tic-tac-toe/</guid><description>&lt;p&gt;Created it using Google Gemini CLI on 2nd Jul 2025
&lt;link rel="stylesheet" href="https://vijay.eu/css/tic-tac-toe.css"&gt;
&lt;div class="tic-tac-toe-container"&gt;
&lt;div id="game-setup"&gt;
&lt;div class="setup-section"&gt;
&lt;p class="setup-title"&gt;Game Mode&lt;/p&gt;
&lt;div class="button-group" id="game-mode-selector"&gt;
&lt;button class="btn-toggle active" data-mode="pvc"&gt;Player vs. Computer&lt;/button&gt;
&lt;button class="btn-toggle" data-mode="pvp"&gt;Player vs. Player&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="setup-section" id="first-move-container"&gt;
&lt;p class="setup-title"&gt;Who Goes First?&lt;/p&gt;
&lt;div class="button-group" id="first-move-selector"&gt;
&lt;button class="btn-toggle active" data-first-move="player"&gt;Player (X)&lt;/button&gt;
&lt;button class="btn-toggle" data-first-move="computer"&gt;Computer (O)&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;button id="start-game"&gt;Start Game&lt;/button&gt;
&lt;/div&gt;
&lt;div id="game-board" class="hidden"&gt;
&lt;div class="cell" data-index="0"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="1"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="2"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="3"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="4"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="5"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="6"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="7"&gt;&lt;/div&gt;
&lt;div class="cell" data-index="8"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id="message" class="hidden"&gt;&lt;/div&gt;
&lt;button id="restart-game" class="hidden"&gt;Restart Game&lt;/button&gt;
&lt;/div&gt;
&lt;script src="https://vijay.eu/js/tic-tac-toe.js"&gt;&lt;/script&gt;
&lt;/p&gt;</description></item><item><title>Decibel Meter</title><link>https://vijay.eu/projects/decibel-meter/</link><pubDate>Wed, 12 Mar 2025 23:28:14 +0200</pubDate><guid>https://vijay.eu/projects/decibel-meter/</guid><description>&lt;p&gt;Built using Anthropic Claude Code CLI.&lt;/p&gt;
&lt;div class="decibel-meter-wrapper"&gt;
&lt;div class="speedometer-container"&gt;
&lt;div class="needle-container"&gt;
&lt;div class="needle" id="needle"&gt;&lt;/div&gt;
&lt;div class="needle-center"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="reading" id="reading"&gt;0 dB&lt;/div&gt;
&lt;div class="description" id="description"&gt;Please enable microphone&lt;/div&gt;
&lt;button id="startButton"&gt;Start Measuring&lt;/button&gt;
&lt;div class="sensitivity-control"&gt;
&lt;label for="sensitivity"&gt;Microphone Sensitivity: &lt;span id="sensitivityValue"&gt;&lt;/span&gt;&lt;/label&gt;
&lt;input type="range" id="sensitivity" min="1" max="5" step="0.5" value="2.0"&gt;
&lt;/div&gt;
&lt;div class="error" id="errorMessage"&gt;&lt;/div&gt;
&lt;div class="child-voice-legend"&gt;
&lt;div class="child-voice-title"&gt;Voice Level Guide for Kids&lt;/div&gt;
&lt;div class="voice-level-item"&gt;
&lt;div class="voice-level-color" style="background: #4CAF50;"&gt;&lt;/div&gt;
&lt;span class="voice-level-text"&gt;Whisper to Library Voice (0-40 dB)&lt;/span&gt;
&lt;/div&gt;
&lt;div class="voice-level-item"&gt;
&lt;div class="voice-level-color" style="background: #7CB342;"&gt;&lt;/div&gt;
&lt;span class="voice-level-text"&gt;Indoor Voice (40-60 dB)&lt;/span&gt;
&lt;/div&gt;
&lt;div class="voice-level-item"&gt;
&lt;div class="voice-level-color" style="background: #FFC107;"&gt;&lt;/div&gt;
&lt;span class="voice-level-text"&gt;Classroom Voice (60-70 dB)&lt;/span&gt;
&lt;/div&gt;
&lt;div class="voice-level-item"&gt;
&lt;div class="voice-level-color" style="background: #FF9800;"&gt;&lt;/div&gt;
&lt;span class="voice-level-text"&gt;Playground Voice (70-80 dB)&lt;/span&gt;
&lt;/div&gt;
&lt;div class="voice-level-item"&gt;
&lt;div class="voice-level-color" style="background: #FF5722;"&gt;&lt;/div&gt;
&lt;span class="voice-level-text"&gt;Too Loud! (80+ dB)&lt;/span&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;script src="https://vijay.eu/js/decibel-meter.js"&gt;&lt;/script&gt;
&lt;link rel="stylesheet" href="https://vijay.eu/css/decibel-meter.css"&gt;
&lt;ul&gt;
&lt;li&gt;No data is stored or transmitted - all processing happens locally in your browser.&lt;/li&gt;
&lt;li&gt;dB values are approximate. Calibration has not been done.&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>