<?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: Tuan (Felix) Nguyen</title>
    <description>The latest articles on DEV Community by Tuan (Felix) Nguyen (@felixdoit).</description>
    <link>https://dev.to/felixdoit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3958488%2F5c6feddb-1877-4268-90af-6f60a48b1faf.jpg</url>
      <title>DEV Community: Tuan (Felix) Nguyen</title>
      <link>https://dev.to/felixdoit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/felixdoit"/>
    <language>en</language>
    <item>
      <title>Dấu or Die: Can LLMs put the accents back on Vietnamese?</title>
      <dc:creator>Tuan (Felix) Nguyen</dc:creator>
      <pubDate>Sun, 11 Oct 2026 19:33:32 +0000</pubDate>
      <link>https://dev.to/felixdoit/dau-or-die-can-llms-put-the-accents-back-on-vietnamese-1k8c</link>
      <guid>https://dev.to/felixdoit/dau-or-die-can-llms-put-the-accents-back-on-vietnamese-1k8c</guid>
      <description>&lt;p&gt;I built &lt;strong&gt;Dấu or Die&lt;/strong&gt;, a Kaggle benchmark that removes the diacritics from Vietnamese sentences and asks LLMs to put them back. Each missing tone mark can change a word's meaning completely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benchmark:&lt;/strong&gt; &lt;a href="https://www.kaggle.com/benchmarks/tuanfelixnguyen/du-or-die-vietnamese-diacritic-restoration" rel="noopener noreferrer"&gt;https://www.kaggle.com/benchmarks/tuanfelixnguyen/du-or-die-vietnamese-diacritic-restoration&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Task &amp;amp; code:&lt;/strong&gt; &lt;a href="https://www.kaggle.com/benchmarks/tasks/tuanfelixnguyen/dau-or-die-vietnamese-diacritics" rel="noopener noreferrer"&gt;https://www.kaggle.com/benchmarks/tasks/tuanfelixnguyen/dau-or-die-vietnamese-diacritics&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Vietnamese has six tones plus vowel marks (ă, â, ê, ô, ơ, ư, đ). Millions of us still type &lt;strong&gt;without them&lt;/strong&gt; ("không dấu"):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on phones without a Vietnamese keyboard,&lt;/li&gt;
&lt;li&gt;in URLs, file names and usernames,&lt;/li&gt;
&lt;li&gt;in old databases and SMS systems that only accept ASCII.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remove the marks and many words collapse into the same string. &lt;code&gt;ban&lt;/code&gt; could be &lt;strong&gt;bàn&lt;/strong&gt; (table), &lt;strong&gt;bán&lt;/strong&gt; (sell), &lt;strong&gt;bạn&lt;/strong&gt; (friend) or &lt;strong&gt;bận&lt;/strong&gt; (busy). Native speakers fix this from context without noticing. I wanted to know whether LLMs can do the same, or whether they just default to the most common word.&lt;/p&gt;

&lt;p&gt;My favourite test sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Cai ban nay ban bao nhieu tien vay ban?&lt;/code&gt;&lt;br&gt;
→ &lt;strong&gt;Cái bàn này bán bao nhiêu tiền vậy bạn?&lt;/strong&gt; ("How much is this table, friend?")&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same three letters appear three times, and each one must become a different word.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the benchmark works
&lt;/h2&gt;

&lt;p&gt;I wrote &lt;strong&gt;37 sentences&lt;/strong&gt; by hand, in 5 categories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;What it tests&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;everyday&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain daily sentences (the baseline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ambiguous&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sentences built around colliding words (bàn/bán/bạn/bận, cười/cưới, ốm/ôm, thấy/thầy, cá/cà/cả…)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;idiom&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Proverbs. The words are fixed, so this needs cultural memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;code_mixed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Viet + English dev slang ("Em deploy code lên server rồi…"). English words must &lt;strong&gt;stay untouched&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proper_noun&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Places and people: Hà Nội, Đà Nẵng, Nguyễn Du, Truyện Kiều…&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Then for each sentence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The diacritics are stripped automatically with Unicode NFD normalisation, and &lt;code&gt;đ&lt;/code&gt; is mapped to &lt;code&gt;d&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The model is asked to restore the text and must return structured output &lt;code&gt;{restored_text: str}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoring is fully deterministic&lt;/strong&gt;, with no LLM judge:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Headline score:&lt;/strong&gt; the share of sentences restored &lt;em&gt;exactly&lt;/em&gt;. One wrong tone counts as wrong, because one wrong tone can change the meaning.&lt;/li&gt;
&lt;li&gt;Also reported: word-level accuracy per category.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Built with the &lt;code&gt;kaggle-benchmarks&lt;/code&gt; library: one &lt;code&gt;@kbench.task&lt;/code&gt; per sentence, aggregated by a parent task via &lt;code&gt;.evaluate()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Models tested
&lt;/h2&gt;

&lt;p&gt;I picked seven models from five different labs, mixing "fast/cheap" tiers with larger models. I wanted to see whether size or lab matters more for a low-resource-ish language task.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Score (exact sentences)&lt;/th&gt;
&lt;th&gt;Sentences right&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Gemini 3.7 Flash&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.00&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;37 / 37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;GLM-5&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;34 / 37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Gemini 2.5 Pro&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;34 / 37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;GPT-5.4 mini&lt;/td&gt;
&lt;td&gt;0.89&lt;/td&gt;
&lt;td&gt;33 / 37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;0.84&lt;/td&gt;
&lt;td&gt;31 / 37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Qwen 3 Coder 480B&lt;/td&gt;
&lt;td&gt;0.68&lt;/td&gt;
&lt;td&gt;25 / 37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Gemma 4 26B (A4B)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;run errored&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I also added Gemma 4 26B, Google's open-weights model, but its run ended in an error before it produced a score. My scoring code assumes at least one sentence comes back in the requested structured format, so a model that fails that on every sample crashes the aggregation. I ran out of time to diagnose it before the deadline. That is a bug in my harness, not a verdict on Gemma.&lt;/p&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. A newer "Flash" model beat an older "Pro" model.&lt;/strong&gt; Gemini 3.7 Flash restored every sentence, while Gemini 2.5 Pro missed 3. For this task, a newer generation mattered more than the bigger tier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Being big doesn't help if the model is specialised for something else.&lt;/strong&gt; Qwen 3 Coder has 480B parameters, by far the largest model here, yet it came last with 25/37. A model tuned for code seems to have lost some fine-grained, multilingual detail. Vietnamese tone marks are exactly that kind of detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The spread is wide even though the task looks trivial.&lt;/strong&gt; Every native speaker restores these sentences without thinking. Yet the gap between the best and worst model is &lt;strong&gt;12 sentences out of 37&lt;/strong&gt;. One wrong tone counts as a wrong sentence, and that is how a reader experiences it too: "bán nhà" (sell the house) and "bàn nhà" (table house) are not "almost the same".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Cost buys very little at the top.&lt;/strong&gt; Kaggle's score-vs-cost chart shows that GPT-5.4 mini reaches 0.89 for roughly $0.02 for the whole benchmark. GLM-5 gains only one more sentence (0.92) but costs over 10× more, around $0.3. Claude Haiku (≈$0.01) and Qwen Coder (&amp;lt;$0.01) are cheaper, but they lose accuracy quickly. For a production "add the accents back" feature, a small model is the sensible choice, with one condition: it has to be the &lt;em&gt;right&lt;/em&gt; small model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What surprised me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The benchmark found its own ambiguity.&lt;/strong&gt; On my first development run, Gemini 3.7 Flash restored &lt;code&gt;Sep bao hop online…&lt;/code&gt; as &lt;strong&gt;"Sếp báo họp online"&lt;/strong&gt; ("the boss &lt;em&gt;announced&lt;/em&gt; a meeting") instead of my reference &lt;strong&gt;"Sếp bảo họp online"&lt;/strong&gt; ("the boss &lt;em&gt;said&lt;/em&gt; to meet"). Both are correct Vietnamese. When I re-ran the same model at temperature 0 for the leaderboard, it chose "bảo" and scored a perfect 1.00.&lt;/p&gt;

&lt;p&gt;Two lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Removing diacritics sometimes destroys information that no amount of context can recover&lt;/strong&gt;, so exact match is a slightly harsh metric. A future version should accept a set of valid answers per sentence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model outputs are not fully stable, even at temperature 0&lt;/strong&gt;, and the wobble shows up exactly on the genuinely ambiguous items. A single leaderboard number hides that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd measure next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fix the harness so format failures count as 0 instead of crashing&lt;/strong&gt;, then re-run Gemma and other open-weights models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple valid answers per sentence&lt;/strong&gt; (bảo/báo), plus &lt;strong&gt;repeated runs&lt;/strong&gt; to measure how stable each model is&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teencode / slang&lt;/strong&gt; ("ko", "dc", "j z") mixed with missing accents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Longer paragraphs&lt;/strong&gt;, where context can help more but errors also add up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regional vocabulary&lt;/strong&gt; (Northern vs Southern words)&lt;/li&gt;
&lt;li&gt;The reverse direction: can a model detect a &lt;em&gt;wrong&lt;/em&gt; diacritic that a typo introduced?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;Solo entry by Tuan Nguyen. Built with &lt;a href="https://github.com/Kaggle/kaggle-benchmarks" rel="noopener noreferrer"&gt;kaggle-benchmarks&lt;/a&gt;. The sentences and the scoring are my own. Feel free to fork the task and add more Vietnamese sentences!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>kagglechallenge</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>I Built StudyNest to Make Scattered Study Notes Easier to Review</title>
      <dc:creator>Tuan (Felix) Nguyen</dc:creator>
      <pubDate>Mon, 05 Oct 2026 07:14:24 +0000</pubDate>
      <link>https://dev.to/felixdoit/i-built-studynest-to-make-scattered-study-notes-easier-to-review-c83</link>
      <guid>https://dev.to/felixdoit/i-built-studynest-to-make-scattered-study-notes-easier-to-review-c83</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;StudyNest turns scattered study notes into a small, usable study space. You can save notes in your browser, ask questions grounded in those notes, generate five practice questions with suggested answers, or get a short summary. A &lt;strong&gt;VI / EN&lt;/strong&gt; switch changes both the interface and the language of the AI response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhslaid4qhdhces7a8e0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhslaid4qhdhces7a8e0.png" alt="StudyNest home page in English with the VI/EN language switch" width="799" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built it during the Hacktoberfest Weekend Challenge and tested it myself with a Python lesson. I asked, “Why does &lt;code&gt;result&lt;/code&gt; equal 34?” StudyNest explained that &lt;code&gt;append(10)&lt;/code&gt; changes &lt;code&gt;[7, 8, 9]&lt;/code&gt; to &lt;code&gt;[7, 8, 9, 10]&lt;/code&gt;, and &lt;code&gt;7 + 8 + 9 + 10 = 34&lt;/code&gt;. That answer came from the note rather than from an unrelated explanation of Python.&lt;/p&gt;

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

&lt;p&gt;I have not handed it to a friend yet, so I cannot claim feedback from someone else. For now, the real test is my own study workflow and yes, since I don't have any friends so I am still looking for a friend to give it to XDDD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open AI matters here
&lt;/h2&gt;

&lt;p&gt;StudyNest uses the open-weight &lt;code&gt;openai/gpt-oss-20b&lt;/code&gt; model through Groq's API. I originally tried running a model locally, but the download was too large for the free space on my computer. Serving an open-weight model through Groq kept the project usable without that download. The model can be changed through configuration without changing the study workflow.&lt;/p&gt;

&lt;p&gt;There is a tradeoff: notes stay in the browser until you request an AI feature, but then their content is sent to Groq for inference. The app says this clearly in the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The front end is a single HTML file. A small Python HTTP server serves it and calls Groq through the official Python SDK. Notes are stored in browser &lt;code&gt;localStorage&lt;/code&gt;. For each AI request, the server sends the notes as reference material and instructs the model to answer only from them or say when they do not contain the answer. The language switch is included in that request, so a note can be reviewed in Vietnamese or English.&lt;/p&gt;

&lt;p&gt;The Groq key is read from a local &lt;code&gt;config.py&lt;/code&gt; file or a deployment environment variable. &lt;code&gt;config.py&lt;/code&gt; is ignored by Git; &lt;code&gt;config_example.py&lt;/code&gt; shows the configuration format. The public demo also limits AI requests to 30 per hour to keep shared usage bounded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo and source code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hacktobefest2026-studynest.onrender.com/" rel="noopener noreferrer"&gt;Try the live demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/AIVIETNAM-AIO-felixdoit/hacktobefest2026-challenge1" rel="noopener noreferrer"&gt;Browse the source code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned and what comes next
&lt;/h2&gt;

&lt;p&gt;A useful AI project can be small: a clear input, a grounded response, and a way to check what the model says. The hardest practical constraint was making the app run on the computer I had. Next I want to add note export and an interactive quiz where learners answer before seeing the suggested answers.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Decision Lab: Making tradeoffs visible with Sanity</title>
      <dc:creator>Tuan (Felix) Nguyen</dc:creator>
      <pubDate>Sun, 04 Oct 2026 15:50:27 +0000</pubDate>
      <link>https://dev.to/felixdoit/decision-lab-making-tradeoffs-visible-with-sanity-2fbm</link>
      <guid>https://dev.to/felixdoit/decision-lab-making-tradeoffs-visible-with-sanity-2fbm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path Two: Vibe-Code Something Strange&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Decision Lab is a small workspace for choices that have no obvious winner. Instead of asking one question and returning a confident answer, it lets people describe the options, name the criteria that matter, assign scores, and then move the importance sliders to see whether their conclusion survives a change in priorities.&lt;/p&gt;

&lt;p&gt;The demo compares two project ideas: &lt;strong&gt;AI Study Planner&lt;/strong&gt; and &lt;strong&gt;Portfolio Website&lt;/strong&gt;. The published Sanity content gives the former a higher learning score and the latter a much faster path to completion. With the initial weights, Portfolio Website leads &lt;strong&gt;78% to 73%&lt;/strong&gt;. The interesting part is the what-if interaction: the answer can change when the user changes what matters most.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://decision-lab-sanity.netlify.app/" rel="noopener noreferrer"&gt;Decision Lab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The published &lt;strong&gt;What should I build next?&lt;/strong&gt; decision is the Sanity-backed example in the sidebar. The screenshots below follow the built-in &lt;strong&gt;Where should I work next?&lt;/strong&gt; example, which uses three options to show more of the comparison interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78x4qivcypf9z7fkzghi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78x4qivcypf9z7fkzghi.png" alt="Decision Lab overview showing Join a larger team as the front runner in a career decision" width="800" height="365"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Overview: the current front runner and the three available paths.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9iefgpm1dwp5x386z1fm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9iefgpm1dwp5x386z1fm.png" alt="Three career option cards with fit scores of 79, 78, and 71 percent above the priority sliders" width="799" height="366"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;At a glance: option scores and the sliders used to test different priorities.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl33ibnflhpynxp13yyp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl33ibnflhpynxp13yyp3.png" alt="Decision Lab side-by-side comparison showing criterion scores and reasoning for three career options" width="800" height="368"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Side by side: each option is scored against the same criteria.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0onxpfjnjs4rzzzauwib.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0onxpfjnjs4rzzzauwib.png" alt="Lower rows of the career comparison and interactive priority sliders" width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The rest of the breakdown, with weights that can be adjusted without editing published content.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No login is needed to view the published decision. The app's &lt;strong&gt;New decision&lt;/strong&gt; flow creates a local browser draft; published content is edited in Sanity Studio.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/AIVIETNAM-AIO-felixdoit/decision-lab" rel="noopener noreferrer"&gt;Decision Lab source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The frontend is React, TypeScript, and Vite. Sanity Studio and the content schemas live in the same repository. The frontend queries published documents from the public Sanity dataset with GROQ. No API token is shipped to the browser.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Build Process
&lt;/h2&gt;

&lt;p&gt;I built this with Codex as an iterative coding partner. We started with a narrow scope: one decision, several options, criteria with weights, and a side-by-side comparison. This avoided a generic dashboard full of unrelated features and made the structured content central to the experience.&lt;/p&gt;

&lt;p&gt;The first pass used a local example so the interface and scoring could be tested before the Sanity project existed. Then I created a Sanity project, connected the Studio schema, added the frontend origin to CORS, and entered a real decision. The content model has a &lt;code&gt;decision&lt;/code&gt; document containing &lt;code&gt;criteria&lt;/code&gt; and &lt;code&gt;options&lt;/code&gt;; each option has a score and optional reasoning for each criterion. The app calculates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fit = sum(score * criterion weight) / (5 * sum(criterion weight)) * 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first real publish revealed a useful failure: the document had options but no scores yet. The API returned &lt;code&gt;null&lt;/code&gt; for the score arrays. I updated the query layer to normalize missing arrays, changed the interface to ask for scores instead of inventing a winner, and added Studio validation requiring scores. That was more valuable than a happy-path screenshot: it made the app honest about incomplete content.&lt;/p&gt;

&lt;p&gt;We also caught a small CSS specificity issue during visual review. The initial letters in the comparison table's colored option marks were pushed away from the center by a generic table-header rule. A narrower rule fixed the alignment. Testing with real Sanity content and a human looking at the page found both issues.&lt;/p&gt;

&lt;p&gt;The app deliberately keeps importance sliders as temporary what-if controls. Moving them recalculates the ranking instantly without changing the published decision. This separates the shared source data from an individual visitor's exploration.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Sanity
&lt;/h2&gt;

&lt;p&gt;Sanity holds the decision question, context, category, criteria, option summaries, colors, scores, and evidence notes as structured content. Studio is the editing interface; the frontend queries the published dataset and renders the same data in both the overview and comparison views. Editing and republishing a decision in Studio changes what visitors see without rebuilding the frontend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sanity Project ID:&lt;/strong&gt; &lt;code&gt;f2yoycw9&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dataset:&lt;/strong&gt; &lt;code&gt;production&lt;/code&gt; (public)&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Improve Next
&lt;/h2&gt;

&lt;p&gt;The current frontend reads published Sanity content while its quick-create flow saves drafts locally. A future version could use Sanity's App SDK for authenticated editing and keep drafts in Content Lake. I would also add a richer way to link scores to criteria, so content editors do not have to type the exact criterion name.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
    <item>
      <title>Balan Coffee &amp; Roastery — A Slow-Drip Vietnamese Coffee Landing Page</title>
      <dc:creator>Tuan (Felix) Nguyen</dc:creator>
      <pubDate>Sun, 16 Aug 2026 20:41:25 +0000</pubDate>
      <link>https://dev.to/felixdoit/balan-coffee-roastery-a-slow-drip-vietnamese-coffee-landing-page-33ep</link>
      <guid>https://dev.to/felixdoit/balan-coffee-roastery-a-slow-drip-vietnamese-coffee-landing-page-33ep</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, Perfect Landing&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I created &lt;strong&gt;Balan Coffee &amp;amp; Roastery&lt;/strong&gt;, a polished landing page for a fictional Vietnamese comfort café in Saigon.&lt;/p&gt;

&lt;p&gt;The concept is inspired by the quiet comfort of slow phin coffee, butter toast, and small sweet treats. Rather than treating coffee as a quick purchase, I wanted the site to feel like a calm daily ritual: slow, warm, familiar, and personal.&lt;/p&gt;

&lt;p&gt;Visitors can explore the menu, learn the café story, find visiting information, and interact with a small pixel-art coffee brewing experience.&lt;/p&gt;

&lt;p&gt;Highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Responsive editorial-style coffee shop landing page&lt;/li&gt;
&lt;li&gt;Vietnamese coffee-inspired menu, story, ritual, and visit sections&lt;/li&gt;
&lt;li&gt;Clear navigation and accessible interactive controls&lt;/li&gt;
&lt;li&gt;Consistent number and price typography throughout the site&lt;/li&gt;
&lt;li&gt;A lightweight interactive mini-game: &lt;strong&gt;Pixel Phin Brew&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Dose beans into the phin&lt;/li&gt;
&lt;li&gt;Grind the beans&lt;/li&gt;
&lt;li&gt;Bloom the coffee&lt;/li&gt;
&lt;li&gt;Let the phin drip&lt;/li&gt;
&lt;li&gt;Serve the finished cup&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Built without heavy UI, game, or animation libraries&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://frontend-challenge.boy0388402.workers.dev" rel="noopener noreferrer"&gt;Balan Coffee &amp;amp; Roastery&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/AIVIETNAM-AIO-felixdoit/frontend-challenge" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;I wanted to create something that felt more like a coffee ritual than a typical restaurant landing page.&lt;/p&gt;

&lt;p&gt;The visual direction uses warm cream tones, deep coffee browns, generous spacing, subtle texture, and an editorial layout inspired by a slow morning at a Saigon café. I paid attention to small details such as consistent tabular numerals for prices and opening hours, responsive layouts, visible interaction states, and reduced-motion support.&lt;/p&gt;

&lt;p&gt;The feature I enjoyed building most was &lt;strong&gt;Pixel Phin Brew&lt;/strong&gt;. I wanted the interaction to be understandable instead of just decorative, so each button clearly explains the next brewing action. Every correct step updates the pixel scene, progress indicator, and feedback message until the final cup is served.&lt;/p&gt;

&lt;p&gt;The project was built with React, TypeScript, Vinext/Vite, and custom CSS. I kept the implementation lightweight and avoided adding large dependencies for animation or the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assets and Credits
&lt;/h2&gt;

&lt;p&gt;The Manhattan Café illustration assets retain the original creator watermark visible in the source files. The interface design, responsive layout, styling, interactions, and Pixel Phin Brew game were created for this submission.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Operation Turing — A Caesar Cipher Codebreaking Game</title>
      <dc:creator>Tuan (Felix) Nguyen</dc:creator>
      <pubDate>Sat, 20 Jun 2026 15:18:54 +0000</pubDate>
      <link>https://dev.to/felixdoit/operation-turing-a-caesar-cipher-codebreaking-game-1dga</link>
      <guid>https://dev.to/felixdoit/operation-turing-a-caesar-cipher-codebreaking-game-1dga</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/june-game-jam-2026-06-03"&gt;June Solstice Game Jam&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Operation Turing&lt;/strong&gt; is a Caesar cipher decoding game set in WWII Bletchley Park. You're a codebreaker racing the clock — 60 seconds to decode 5 intercepted enemy messages by finding the correct cipher shift before time runs out.&lt;/p&gt;

&lt;p&gt;The game ties directly into the &lt;strong&gt;Best Ode to Alan Turing&lt;/strong&gt; prize category: Turing's most famous achievement was breaking the Enigma cipher during WWII, so the entire core mechanic — sliding through shift values to reveal a hidden message — is a small, playable echo of the codebreaking work he pioneered. The game closes with one of his quotes after each run.&lt;/p&gt;

&lt;p&gt;Mechanically it's intentionally simple: a slider from 0–25, real-time decode feedback, a hint system that costs points, and a 60-second timer across 5 rounds. I wanted something polished and complete rather than ambitious and half-finished, given the jam's tight turnaround.&lt;/p&gt;

&lt;p&gt;This is actually my first game jam, so I kept things small on purpose — wanted to make sure I had something working end-to-end rather than something ambitious but broken XD.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://aivietnam-aio-felixdoit.github.io/operation-turing/" rel="noopener noreferrer"&gt;▶ Play Operation Turing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/XxqsxLSbm-U" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AIVIETNAM-AIO-felixdoit" rel="noopener noreferrer"&gt;
        AIVIETNAM-AIO-felixdoit
      &lt;/a&gt; / &lt;a href="https://github.com/AIVIETNAM-AIO-felixdoit/operation-turing" rel="noopener noreferrer"&gt;
        operation-turing
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🔐 Operation Turing&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A Caesar cipher decoding game built for the &lt;strong&gt;DEV.to June Solstice Game Jam 2026&lt;/strong&gt;, honoring Alan Turing and the codebreakers of Bletchley Park.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://aivietnam-aio-felixdoit.github.io/operation-turing/" rel="nofollow noopener noreferrer"&gt;▶ Play the game&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b6628fb612fa456732374b21154a147efca9fda3f6e35faf4b732529ed537b19/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d616465253230776974682d48544d4c2532464353532532464a532d626c7565"&gt;&lt;img src="https://camo.githubusercontent.com/b6628fb612fa456732374b21154a147efca9fda3f6e35faf4b732529ed537b19/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d616465253230776974682d48544d4c2532464353532532464a532d626c7565" alt="Made with"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;About&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;WWII, 1942. Enemy messages are flooding in. You're a codebreaker at Bletchley Park, racing against the clock to decode intercepted Caesar cipher transmissions before time runs out.&lt;/p&gt;
&lt;p&gt;This game is an ode to &lt;strong&gt;Alan Turing&lt;/strong&gt; — the man who broke the Enigma cipher and laid the foundations of modern computer science. The mechanic itself (finding the right shift to reveal a hidden message) is a small, playable echo of the codebreaking work Turing pioneered.&lt;/p&gt;
&lt;p&gt;Built for the jam's &lt;strong&gt;Best Ode to Alan Turing&lt;/strong&gt; bonus prize category.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How to play&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Start mission&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Drag the &lt;strong&gt;shift slider&lt;/strong&gt; (0–25) until the decoded text below turns into readable English&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Submit decode&lt;/strong&gt; to lock in your answer&lt;/li&gt;
&lt;li&gt;Decode all 5 messages…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AIVIETNAM-AIO-felixdoit/operation-turing" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;The whole game is vanilla &lt;strong&gt;HTML/CSS/JavaScript&lt;/strong&gt; — no frameworks, no build step, no dependencies. I wanted it to be trivially embeddable and playable directly in the browser for judging.&lt;/p&gt;

&lt;p&gt;A few technical notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Caesar cipher logic&lt;/strong&gt;: a single &lt;code&gt;caesar(text, shift)&lt;/code&gt; function handles both encoding and decoding — decoding a message is just encoding it with the inverse shift (&lt;code&gt;26 - shift&lt;/code&gt;), so I didn't need separate encrypt/decrypt implementations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time decode preview&lt;/strong&gt;: as you drag the shift slider, the decoded text updates live and turns green the instant it matches the target phrase, so you get immediate feedback without needing to "submit" to check your guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoring&lt;/strong&gt;: each correct decode is worth 20 points, or 10 if you used a hint on that round — so hints don't subtract from your score directly, they just halve what you would've earned for that round.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polish pass&lt;/strong&gt;: added small CSS keyframe animations (fade-in on load/new round, a shake on wrong submissions, a green flash on correct ones) to make the feedback loop feel more responsive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: static site on GitHub Pages — push to &lt;code&gt;main&lt;/code&gt;, zero config.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Submitting for &lt;strong&gt;Best Ode to Alan Turing&lt;/strong&gt;. The game's entire premise — decoding intercepted WWII-era cipher messages under time pressure — is a direct nod to Turing's work breaking Enigma at Bletchley Park. The ending screen also surfaces one of his quotes as a small tribute.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gamechallenge</category>
      <category>gamedev</category>
    </item>
  </channel>
</rss>
