<?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: Faris Daffa</title>
    <description>The latest articles on DEV Community by Faris Daffa (@faris0520).</description>
    <link>https://dev.to/faris0520</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F620344%2Fb6f254fc-3ac9-4cac-988d-08c7ecc982f4.webp</url>
      <title>DEV Community: Faris Daffa</title>
      <link>https://dev.to/faris0520</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faris0520"/>
    <language>en</language>
    <item>
      <title>My friend rambles in interviews, so I built him Unramble</title>
      <dc:creator>Faris Daffa</dc:creator>
      <pubDate>Sat, 03 Oct 2026 14:41:30 +0000</pubDate>
      <link>https://dev.to/faris0520/my-friend-rambles-in-interviews-so-i-built-him-unramble-1kf5</link>
      <guid>https://dev.to/faris0520/my-friend-rambles-in-interviews-so-i-built-him-unramble-1kf5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;My friend, Agus, has a problem I recognized because I have it too. He knows his stuff. Ask him about a project he shipped and, on paper, the answer is all there. But ask him out loud, in an interview, and the answer wanders: it starts three digressions deep, never lands the result, and buries the one sentence that mattered somewhere in the middle. He is not a weak candidate. He is an unpracticed one, and practice is exactly the thing he cannot do, because practicing means saying half-finished answers out loud to another human, at night, repeatedly, until it stops being embarrassing.&lt;/p&gt;

&lt;p&gt;So I built him &lt;strong&gt;Unramble&lt;/strong&gt;: a practice partner for real interviews that runs entirely on his laptop. He pastes a job description and a few CV bullets. Gemma 3, running locally through Ollama, writes seven interview questions tuned to that role. He answers out loud, into the microphone. Whisper, running locally inside the app's own server, transcribes him. And then the part he actually needed: the report.&lt;/p&gt;

&lt;p&gt;Every sentence of his answer gets a color that says what job it was doing: blue set the Situation, purple named the Task, green carried the Action, yellow landed the Result. Gray is filler. He can literally watch an answer go green, green, gray, gray, gray, and never reach yellow. Below the transcript: relevance and conciseness scores, STAR coverage, a "tangled" meter computed from measured filler density and sentence length, and a rewritten version of his own answer with the detours removed, keeping only his real facts. Any question can be re-answered on its own and rescored, so the weak one gets drilled until it stops being weak.&lt;/p&gt;

&lt;p&gt;One rule shaped the whole build: nothing he says ever leaves his laptop. The recordings live in browser tab memory and are never written anywhere. The transcript, the scores, the report: all in local storage. The models run on his machine. There is no server to upload to.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://unramble.farisdaffa.in" rel="noopener noreferrer"&gt;https://unramble.farisdaffa.in&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;GitHub: &lt;a href="https://github.com/Faris0520/unramble" rel="noopener noreferrer"&gt;Faris0520/unramble&lt;/a&gt;&lt;/p&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/Faris0520" rel="noopener noreferrer"&gt;
        Faris0520
      &lt;/a&gt; / &lt;a href="https://github.com/Faris0520/unramble" rel="noopener noreferrer"&gt;
        unramble
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A practice partner for real job interviews that runs entirely on your laptop.
    &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;Unramble&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A practice partner for real job interviews that runs entirely on your laptop.&lt;/p&gt;
&lt;p&gt;Unramble was built for one real person: a friend preparing for interviews whose answers know
all the facts but fall apart in the telling. You speak an answer out loud, and the app shows
the structure behind it: which sentence set the scene, which one carried the action, where
you drifted into filler. Then Gemma rewrites the answer, straight.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everything is local.&lt;/strong&gt; Questions and scores come from Gemma 3 4B through Ollama
Transcription comes from Whisper through transformers.js, inside the app's own server
Recordings live in browser tab memory and are never written anywhere. There is no server to
upload to, so an interview rehearsal stays as private as it should be.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;One-time setup (details in &lt;a href="https://github.com/Faris0520/unramble/./SETUP.md" rel="noopener noreferrer"&gt;SETUP.md&lt;/a&gt;):&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;winget install Ollama.Ollama
ollama pull gemma3:4b
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;npm install
npm run build
npm start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="nofollow noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Faris0520/unramble" 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;&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%2Fl9mv1kif6ck408upnvfq.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%2Fl9mv1kif6ck408upnvfq.png" alt="first setup" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three open-source pieces, glued together as one local pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Question generation and scoring: Gemma 3 4B via Ollama.&lt;/strong&gt; The app talks to Ollama's local REST API with structured output (a JSON schema Ollama enforces), which is how a 4B model stays reliable: generation returns exactly &lt;code&gt;{ questions: string[7] }&lt;/code&gt;, and scoring returns sentence tags plus integer scores, every time, on a laptop CPU. The model tag is one environment variable, so he can swap in a bigger Gemma when his hardware allows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transcription: Whisper via transformers.js.&lt;/strong&gt; The browser records the take, decodes it with its own AudioContext, mixes to mono, resamples to 16 kHz, and hands the app's server a plain PCM WAV. The server runs the quantized Whisper model in-process; the weights download once into the project folder and work offline from then on. For Indonesian answers, an environment variable switches to the small model, which hears the language better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The metrics that do not need a model at all.&lt;/strong&gt; Filler counting, sentence splitting, and the tangled score are computed in plain TypeScript from his actual answer. A model judges structure; the app measures facts. The report never shows a number that was not measured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two details I am proud of. First, the transcript is editable before scoring: if Whisper misheard a word, he fixes it, and the score is computed on what he approved, not on what the machine guessed. Second, the on-screen checklist: the setup page pings Ollama, sees exactly which local piece is missing, and prints the one command that fixes it. A friend who would rather not debug a local stack by hand gets that command with a Copy button.&lt;/p&gt;

&lt;p&gt;The interface follows Notion's design system, on purpose: a report about your weakest answer should read like a calm document, not a dashboard. Pastel tints carry the sentence structure, purple is reserved for the one action per screen, and the same layout works on a phone, because practice happens wherever the laptop opens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter
&lt;/h2&gt;

&lt;p&gt;An interview rehearsal is the most private thing a job seeker does. You say half-finished sentences. You forget words you know. You ramble, and you know you are rambling while it is happening. Nobody would want a recording of that on somebody else's server.&lt;/p&gt;

&lt;p&gt;That is the argument for open, local AI in this specific product, and it is not a technical argument, it is a dignity argument. With a closed API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;his voice and his weakest answers would sit in someone else's cloud, on someone else's retention policy;&lt;/li&gt;
&lt;li&gt;every practice rep would cost money, and the whole point of practice is unlimited reps;&lt;/li&gt;
&lt;li&gt;it would need the internet, and the moment he most wants to practice is 2 AM before a morning interview, when he just needs the thing to work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the open stack, the privacy story is inspectable instead of promised. The Ollama process runs on his machine, on a port he can see. The model weights sit in a folder he owns. If he wanted, he could read every line that touches his voice. And because the core is open, the app can grow with him: swap the model when a better open one lands, fine-tune on his own practice answers someday, run it forever at zero cost on hardware he already owns.&lt;/p&gt;

&lt;p&gt;Open innovation also meant &lt;em&gt;I&lt;/em&gt; could build this in a weekend and hand it to one specific person, with no API budget, no accounts, and no server to maintain. The tool exists because one real friend needed it, and it can keep existing on his laptop whether or not I ever touch it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handing it over
&lt;/h2&gt;

&lt;p&gt;Handover happened on his actual laptop, in his room, with the setup page doing its job: he installed Ollama, pulled the model, and watched the checklist tick green one piece at a time. He is a computer science student, so halfway through the download he asked what exactly was running on his machine. That question is the reason this app is local, and this friend is the one person I could not answer with &lt;em&gt;trust me&lt;/em&gt;. So I opened the folders instead: model weights in a directory he owns, transcript and scores in local storage, recordings that are never written anywhere. He can read every line that touches his voice. Most people have to take that on faith. He can just check.&lt;/p&gt;

&lt;p&gt;Then I handed him the microphone and sat where he could not see me, because a first take needs an audience of nobody. He pasted a junior developer posting he had actually applied to, answered the first question out loud, and produced the answer he always produces: every fact present, one long green Action doing most of the talking, the yellow Result arriving late and out of breath. The tangled meter flagged it without mercy. The moment that mattered came when the rewrite rendered, though. The app had kept only his real facts and cut the detours, and he read his own answer, straight, and said the thing this whole project was built for: &lt;em&gt;oh. That is what I meant.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;He drilled the weakest question again that night, on his own, with me out of the room. I know because a screenshot of the second report arrived later with no caption: same question, less gray, the yellow landing where it belongs. That screenshot is the best evidence this project has produced, because it shows the tool in its natural habitat: 2 AM, one nervous person, one laptop, no budget, no account, no me. He is a computer science student. If he ever wants to make it better, he can read the code and change it. That was always the real handover: not a service I keep alive for him, but a folder of open pieces he fully owns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Gemma&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Thanks to the open-source projects it stands on: Gemma, Ollama, Whisper, and transformers.js.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Hi there! Welcome to my Dev.to profile</title>
      <dc:creator>Faris Daffa</dc:creator>
      <pubDate>Wed, 25 Sep 2024 23:23:41 +0000</pubDate>
      <link>https://dev.to/faris0520/hi-there-1ec0</link>
      <guid>https://dev.to/faris0520/hi-there-1ec0</guid>
      <description>&lt;p&gt;Hello, welcome to my Dev.to profile /.\ &lt;/p&gt;

&lt;p&gt;I'm Faris Daffa, 18yo, i live in Jambi City, Indonesia.&lt;/p&gt;

&lt;p&gt;I am currently an undergraduate student at Computer Science or Informatics, Faculty of Science and Technology, Jambi University.&lt;/p&gt;

&lt;p&gt;Nice to meet ya!&lt;/p&gt;

&lt;p&gt;26/09/2024&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
