<?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: Sachindu Nethmin</title>
    <description>The latest articles on DEV Community by Sachindu Nethmin (@sachindu_nethmin).</description>
    <link>https://dev.to/sachindu_nethmin</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%2F3218302%2Fe8a4977e-bf15-48ba-a727-21e4be91c16d.jpg</url>
      <title>DEV Community: Sachindu Nethmin</title>
      <link>https://dev.to/sachindu_nethmin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sachindu_nethmin"/>
    <language>en</language>
    <item>
      <title>SnapCode 🦥: I stopped retyping code from lecture slides. One hotkey, offline, Gemma does the rest</title>
      <dc:creator>Sachindu Nethmin</dc:creator>
      <pubDate>Sun, 25 Oct 2026 05:51:10 +0000</pubDate>
      <link>https://dev.to/sachindu_nethmin/snapcode-i-stopped-retyping-code-from-lecture-slides-one-hotkey-offline-gemma-does-the-rest-1i57</link>
      <guid>https://dev.to/sachindu_nethmin/snapcode-i-stopped-retyping-code-from-lecture-slides-one-hotkey-offline-gemma-does-the-rest-1i57</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge: Week 3&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;In our machine learning practicals, the lecturer shows code on the slides: imputers, pipelines, encoders, one cell after another. We're supposed to run it in Colab ourselves. Until this week, my routine for every single cell was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take a screenshot&lt;/li&gt;
&lt;li&gt;Open it in Google Lens&lt;/li&gt;
&lt;li&gt;Copy the text&lt;/li&gt;
&lt;li&gt;Paste it into Colab&lt;/li&gt;
&lt;li&gt;Fix the indentation Lens threw away, the &lt;code&gt;‘curly quotes’&lt;/code&gt; that break Python, and the &lt;code&gt;[[&lt;/code&gt; that turned into &lt;code&gt;[&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run it, get a &lt;code&gt;SyntaxError&lt;/code&gt;, and go back to step 5&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's about a minute per cell, plus an internet connection the lab Wi-Fi doesn't always give you. A sloth would never.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;SnapCode&lt;/strong&gt;. Press a hotkey, drag a box around the code, and press &lt;strong&gt;Cmd+V&lt;/strong&gt;. The clean, correctly indented code is already on your clipboard. Everything runs on my laptop, with no internet and no account, using open-source OCR and an open-weight model.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa1ty0gzn4kf2azcawk0k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa1ty0gzn4kf2azcawk0k.gif" alt="SnapCode demo: hotkey, drag over a lecture slide, Tesseract + Gemma run locally, Cmd+V into Colab" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Rendered from a real SnapCode run on a slide from my practical. The output and the 12.0 s timing are real; the timer is sped up in the video. &lt;a href="https://github.com/Sachindu-Nethmin/snapcode/blob/main/docs/demo.mp4" rel="noopener noreferrer"&gt;MP4 version&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hotkey → drag → Cmd+V.&lt;/strong&gt; A 🦥 notification tells you when it's ready, for example &lt;em&gt;"24 lines copied via gemma4:E4B in 12.0s"&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before / after: why plain OCR isn't enough
&lt;/h3&gt;

&lt;p&gt;I ran SnapCode on real screenshots from my practical. Here is a slide with three Colab cells (mean, median and mode imputation).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tesseract on its own (open-source OCR, raw output):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mean replacement
12]                  .      ;      ;
ne      from sklearn.impute import SimpleImputer
mean_imputer = SimpleImputer(strategy="mean")
X_train[ ‘Preparation Time _min’] = mean_imputer.fit_transform(
X_train[[‘Preparation_Time_min’]]
)
X_test[‘Preparation_Time_min’] = mean_imputer.transform(
X_test[[ ‘Preparation _Time_min*]]
)
Median replacement
3)         a         a                      7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Cell numbers and headings are mixed into the code. The column name is misread two different ways (&lt;code&gt;Preparation Time _min&lt;/code&gt;, &lt;code&gt;Preparation _Time_min*&lt;/code&gt;). The quotes are curly, and all indentation is gone. Pasting this into Colab gives you errors to fix, not working code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SnapCode (Tesseract + Gemma):&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.impute&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleImputer&lt;/span&gt;
&lt;span class="n"&gt;mean_imputer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleImputer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mean&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Preparation_Time_min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mean_imputer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Preparation_Time_min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Preparation_Time_min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mean_imputer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Preparation_Time_min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;median_imputer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleImputer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;median&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Distance_km&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;median_imputer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Distance_km&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ...mode imputer cell, also exact
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It matches the slide exactly. Here's a second one, a &lt;code&gt;ColumnTransformer&lt;/code&gt; pipeline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tesseract:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;numeric features = X_train.select_dtypes(include=[‘int64', ‘float64']).columns
numeric pipeline = Pipeline([
(‘imputer*, SimpleImputer(strategy="median")),
(‘scaler’, StandardScaler())
])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;SnapCode:&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="n"&gt;numeric_features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_dtypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;int64&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;float64&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;
&lt;span class="n"&gt;numeric_pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;imputer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SimpleImputer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;median&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scaler&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StandardScaler&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;h3&gt;
  
  
  Results on 4 real lecture screenshots
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Screenshot&lt;/th&gt;
&lt;th&gt;Lines&lt;/th&gt;
&lt;th&gt;Exact match?&lt;/th&gt;
&lt;th&gt;Time (Apple M5, offline)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3 imputer cells&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.6 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ColumnTransformer pipeline&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;12.0 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OneHotEncoder cell&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;4.1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-line &lt;code&gt;print&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;1.3 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  The lazy payoff
&lt;/h3&gt;

&lt;p&gt;My old Google Lens routine took about &lt;strong&gt;60–90 seconds per cell&lt;/strong&gt;, counting the fixes. A practical has around 15–20 cells, so that's &lt;strong&gt;15–25 minutes per session&lt;/strong&gt; spent copying code instead of understanding it. With SnapCode it's a hotkey and a 5–15 second wait, during which I don't have to do anything. That's real sloth energy. 🦥&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/Sachindu-Nethmin" rel="noopener noreferrer"&gt;
        Sachindu-Nethmin
      &lt;/a&gt; / &lt;a href="https://github.com/Sachindu-Nethmin/snapcode" rel="noopener noreferrer"&gt;
        snapcode
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Hotkey → drag over code on screen → Cmd+V. Offline OCR with Tesseract + Gemma via Ollama 🦥
    &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;SnapCode 🦥&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Sachindu-Nethmin/snapcode/docs/demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2FSachindu-Nethmin%2Fsnapcode%2FHEAD%2Fdocs%2Fdemo.gif" alt="SnapCode demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press a hotkey, drag a box around code on a lecture slide or video, then press Cmd+V. The clean, correctly indented code is already on your clipboard.&lt;/p&gt;

&lt;p&gt;Everything runs locally and works without internet:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;table&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;th&gt;Step&lt;/th&gt;

&lt;th&gt;Tool&lt;/th&gt;

&lt;th&gt;Why&lt;/th&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;Select region&lt;/td&gt;

&lt;td&gt;macOS &lt;code&gt;screencapture -i&lt;/code&gt;&lt;br&gt;
&lt;/td&gt;

&lt;td&gt;built in&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Rough OCR&lt;/td&gt;

&lt;td&gt;
&lt;br&gt;
&lt;a href="https://github.com/tesseract-ocr/tesseract" rel="noopener noreferrer"&gt;Tesseract&lt;/a&gt; (Apache-2.0)&lt;/td&gt;

&lt;td&gt;fast first draft&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Fix + re-indent&lt;/td&gt;

&lt;td&gt;
&lt;br&gt;
&lt;strong&gt;Gemma&lt;/strong&gt; (open weights) via &lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;br&gt;
&lt;/td&gt;

&lt;td&gt;reads the image, fixes &lt;code&gt;[[ ]]&lt;/code&gt;, quotes, underscores, indentation&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Verify&lt;/td&gt;

&lt;td&gt;Python &lt;code&gt;ast&lt;/code&gt;&lt;br&gt;
&lt;/td&gt;

&lt;td&gt;on a syntax error, Gemma gets one retry with the error message&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Paste&lt;/td&gt;

&lt;td&gt;&lt;code&gt;pbcopy&lt;/code&gt;&lt;/td&gt;

&lt;td&gt;Cmd+V into Colab or your IDE&lt;/td&gt;

&lt;/tr&gt;

&lt;/tbody&gt;

&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;brew install tesseract ollama
ollama pull gemma4:E4B&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Hotkey (Raycast): Settings → Extensions → Script Commands → &lt;strong&gt;Add Directory&lt;/strong&gt; → choose &lt;code&gt;snapcode/raycast&lt;/code&gt;, then give &lt;strong&gt;SnapCode&lt;/strong&gt; a hotkey (for example ⌥⇧C). The first run asks for Screen Recording permission for Raycast.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;CLI&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;python3 snapcode.py                 &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; select a region&lt;/span&gt;
python3 snapcode.py --image a.png   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; use an existing&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Sachindu-Nethmin/snapcode" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The whole thing is one Python file (standard library only, no &lt;code&gt;pip install&lt;/code&gt;) plus a Raycast script command for the hotkey. Try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;tesseract ollama
ollama pull gemma4:E4B
python3 snapcode.py   &lt;span class="c"&gt;# select a region; the code lands on your clipboard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the hotkey, add the repo's &lt;code&gt;raycast/&lt;/code&gt; folder under Raycast → Script Commands and give &lt;strong&gt;SnapCode&lt;/strong&gt; a shortcut.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hotkey (Raycast) → screencapture -i → Tesseract → Gemma (Ollama) → ast check → pbcopy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Select a region.&lt;/strong&gt; macOS's built-in &lt;code&gt;screencapture -i&lt;/code&gt; gives the drag-a-box picker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rough draft with Tesseract&lt;/strong&gt; (&lt;code&gt;--psm 6&lt;/code&gt;, &lt;code&gt;preserve_interword_spaces=1&lt;/code&gt;). It's fast, but as you saw above, it doesn't understand code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma fixes it.&lt;/strong&gt; I send &lt;em&gt;both&lt;/em&gt; the screenshot and Tesseract's draft to &lt;strong&gt;Gemma 4 E4B&lt;/strong&gt; running locally in &lt;strong&gt;Ollama&lt;/strong&gt;. The prompt says the image is the source of truth: output only the code, restore 4-space indentation, fix OCR confusions (&lt;code&gt;[[ ]]&lt;/code&gt;, quotes, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;0/O&lt;/code&gt;, &lt;code&gt;1/l&lt;/code&gt;), and don't add, rename or "improve" anything. Thinking is off and temperature is 0, because this is transcription, not creativity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-check.&lt;/strong&gt; The output goes through Python's &lt;code&gt;ast.parse&lt;/code&gt; (ignoring notebook lines like &lt;code&gt;!pip&lt;/code&gt; and &lt;code&gt;%time&lt;/code&gt;). If it doesn't parse, Gemma gets one retry with the exact error message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clipboard.&lt;/strong&gt; &lt;code&gt;pbcopy&lt;/code&gt;, plus a macOS notification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If Ollama isn't running, SnapCode still copies Tesseract's draft, so the hotkey never just fails silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why both Tesseract &lt;em&gt;and&lt;/em&gt; a vision model?&lt;/strong&gt; Gemma can read the image alone, but giving it the draft too works like a second pair of eyes. It anchors long identifiers like &lt;code&gt;Preparation_Time_min&lt;/code&gt; and leaves Gemma to do what it's good at: understanding that this is Python and putting the structure back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open Innovation Matters Here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works in the lab with no internet.&lt;/strong&gt; Practical rooms have bad Wi-Fi. SnapCode runs entirely on my laptop. Google Lens needs a connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lecture material stays on my machine.&lt;/strong&gt; Course slides aren't mine to upload to someone else's server. With a local model, nothing leaves the laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing per snap.&lt;/strong&gt; I'll press this hotkey hundreds of times a semester. With a cloud vision API, that's a bill or a rate limit. With Gemma on Ollama it's free forever after one &lt;code&gt;ollama pull&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can swap the model.&lt;/strong&gt; &lt;code&gt;SNAPCODE_MODEL=qwen2.5vl&lt;/code&gt; switches models in one line, so I can pick whichever model reads my lecturer's slide theme best instead of accepting whatever a closed API gives me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every piece is open:&lt;/strong&gt; Tesseract (Apache-2.0), Ollama (MIT) and Gemma (open weights). A friend can run the same setup without signing up for anything.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; Gemma 4 is the step that turns broken OCR into code you can run. Its vision input lets it check every character against the screenshot, and it runs fully offline through Ollama.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>StudyInk - my lecture slides, rewritten as a study book by Gemma on my laptop</title>
      <dc:creator>Sachindu Nethmin</dc:creator>
      <pubDate>Tue, 13 Oct 2026 06:33:59 +0000</pubDate>
      <link>https://dev.to/sachindu_nethmin/studyink-my-lecture-slides-rewritten-as-a-study-book-by-gemma-on-my-laptop-hnd</link>
      <guid>https://dev.to/sachindu_nethmin/studyink-my-lecture-slides-rewritten-as-a-study-book-by-gemma-on-my-laptop-hnd</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I'm doing my honours degree, and every week brings another deck of fifty slides. Slides are made for being &lt;em&gt;presented&lt;/em&gt;, not for revising from: half the meaning is in what the lecturer said, the diagrams are scattered across the deck, and the night before an exam nobody wants to click through 300 of them.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;StudyInk&lt;/strong&gt;. You drop in a lecture deck (&lt;code&gt;.pptx&lt;/code&gt; or &lt;code&gt;.pdf&lt;/code&gt;) and it becomes a chapter of a study book: a spiral notebook with an index tab for each subject, pages that turn, and notes written to be &lt;em&gt;scanned&lt;/em&gt;, not read.&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%2Flalp1eljwvuqnkxmsu27.jpg" 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%2Flalp1eljwvuqnkxmsu27.jpg" alt="The StudyInk study book, closed" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay for it?&lt;/strong&gt; Students like me, around exam time. A cloud AI tool would charge per page or per month, which is exactly when students can't afford one. StudyInk runs on the laptop the student already owns, so it costs nothing per lecture. That makes a one-off price per semester (or a campus licence) viable, where an API bill wouldn't be. Tutors and lecturers are the other customers: a lecturer could hand out a study book for their own course, made from their own slides, without uploading them anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to a deck
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. It's read on the laptop.&lt;/strong&gt; A &lt;code&gt;.pptx&lt;/code&gt; is printed to PDF by LibreOffice (headless), then PyMuPDF reads every slide's text, speaker notes and a small picture of the slide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The figures are cut out.&lt;/strong&gt; Slides have no "Figure 3:" captions to anchor on, so StudyInk finds figures by what they're made of: pictures placed on a slide (minus anything repeated on every slide, like the faculty logo and the background), and clusters of vector shapes that look like a diagram rather than a grid of text boxes.&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%2F52vgkitmvcsxbgqfsayd.jpg" 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%2F52vgkitmvcsxbgqfsayd.jpg" alt="Adding a lecture: the deck is read, the figures cut out, and Gemma looks at each one" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Gemma looks at every figure.&lt;/strong&gt; This is the vision half of Gemma 4. For each crop it decides: diagram, flowchart, table, chart or screenshot (keep it, and caption it), or layout, logo or decoration (set it aside). If a "figure" turns out to be a &lt;em&gt;picture of text&lt;/em&gt; (one of my lecturer's slides had its bullet list pasted in as a screenshot, with zero extractable words), Gemma reads the words off it so they still reach the notes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Gemma names the chapter and writes it,&lt;/strong&gt; a few slides at a time, streaming onto the page while you watch:&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%2F5p5ifh0qo3qtamuodc4w.jpg" 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%2F5p5ifh0qo3qtamuodc4w.jpg" alt="A chapter opener: the title, in a nutshell, and what you'll be able to do" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every section is built to be skimmed: &lt;strong&gt;the gist&lt;/strong&gt; highlighted, &lt;strong&gt;short points&lt;/strong&gt; with the details underneath, &lt;strong&gt;step flows&lt;/strong&gt; (numbered boxes joined by arrows) when the slides describe a process, and &lt;strong&gt;comparison tables&lt;/strong&gt; when they contrast things. Then key terms on an index card, one thing to remember on a sticky note, and "check yourself" questions with the answers folded away until you've tried.&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%2Fyetkxszymdq2fdcxslan.jpg" 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%2Fyetkxszymdq2fdcxslan.jpg" alt="Visual notes: the gist, short points and a step flow" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A figure taped in, with what to notice&lt;/th&gt;
&lt;th&gt;A comparison becomes a table&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2F2popw750iykheqdftnzf.jpg" alt="A figure Gemma kept and captioned" width="800" height="500"&gt;&lt;/td&gt;
&lt;td&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%2F8hkz3ktkgyaz6g1a7cgx.jpg" alt="A comparison table Gemma wrote from the slides" width="800" height="500"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;5. Every point has a footnote.&lt;/strong&gt; Gemma tags each point with the slide it came from (&lt;code&gt;[s12]&lt;/code&gt;), and the book turns that into a numbered footnote. Click one and the actual slide opens, so you can always check the model's work against the source.&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%2F1ak50l0x63tbjsv2z575.jpg" 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%2F1ak50l0x63tbjsv2z575.jpg" alt="A footnote opens the slide it came from" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;▶ &lt;strong&gt;Watch the demo:&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents: a tab for every subject&lt;/th&gt;
&lt;th&gt;On a phone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2F6jcwa56npofezadbe4va.jpg" alt="The contents page with chapters per subject" width="800" height="500"&gt;&lt;/td&gt;
&lt;td&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%2F7cw4922d6qmb5lytq0ov.jpg" alt="A page of notes on a phone" width="700" height="1515"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It also installs as a &lt;strong&gt;Mac app&lt;/strong&gt;: a native window around the notebook, with Node bundled inside, so a student never sees a terminal. It starts Ollama if it isn't running, and when you quit it asks Ollama to unload the model, so the memory comes back.&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%2Fwm6td8cbntle955z1vln.jpg" 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%2Fwm6td8cbntle955z1vln.jpg" alt="StudyInk as a Mac app" width="800" height="450"&gt;&lt;/a&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/Sachindu-Nethmin" rel="noopener noreferrer"&gt;
        Sachindu-Nethmin
      &lt;/a&gt; / &lt;a href="https://github.com/Sachindu-Nethmin/studyink" rel="noopener noreferrer"&gt;
        studyink
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Lecture slides in, a study book out. Gemma 4 on your own laptop turns .pptx/.pdf lecture decks into a notebook of visual revision notes, with a footnote back to every slide.
    &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;StudyInk&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Lecture slides in, a study book out.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Sachindu-Nethmin/studyink/docs/images/04-points.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FSachindu-Nethmin%2Fstudyink%2FHEAD%2Fdocs%2Fimages%2F04-points.jpg" alt="A chapter of StudyInk: the gist, short points with footnotes, and a step flow"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Drop in a lecture deck (&lt;code&gt;.pptx&lt;/code&gt; or &lt;code&gt;.pdf&lt;/code&gt;). StudyInk cuts the figures out of the slides, an open-weight model (Gemma 4, running in Ollama on your own laptop) looks at every figure and writes the lecture up as pages of a study book. Every point has a footnote that points back to the slide it came from.&lt;/p&gt;
&lt;p&gt;It looks and behaves like a spiral notebook: an index tab along the top for each subject, chapters that turn like pages, and notes built to be scanned rather than read. Each section has a highlighted gist, short pen-arrow points, numbered step flows, comparison tables, figures taped in with a caption, key terms on an index card, a sticky note to remember, and self-check questions whose answers stay folded until you've tried.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does with a deck&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reads the slides on your&lt;/strong&gt;…&lt;/p&gt;
&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/Sachindu-Nethmin/studyink" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma4:e4b
pip3 &lt;span class="nb"&gt;install &lt;/span&gt;pymupdf pillow python-pptx
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;code&gt;packaging/build_app.sh&lt;/code&gt; builds &lt;code&gt;StudyInk.app&lt;/code&gt; and a &lt;code&gt;.dmg&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The model is Gemma 4 E4B, running on my laptop through Ollama, and it does two jobs: it &lt;em&gt;sees&lt;/em&gt; and it &lt;em&gt;writes&lt;/em&gt;.&lt;/strong&gt; The same model judges every figure crop (vision) and writes the notes, so there's one download and one thing to keep in memory. Next.js serves the notebook, and its API routes are thin, stateless pass-throughs: one streams the slide reader's progress, one talks to &lt;code&gt;localhost:11434&lt;/code&gt;. The study book lives in the browser's IndexedDB, and the uploaded deck is deleted from the temp folder as soon as it's read.&lt;/p&gt;

&lt;p&gt;The slide reader is a Python script (PyMuPDF, python-pptx, and LibreOffice for &lt;code&gt;.pptx&lt;/code&gt;). Its figure finder is adapted from PaperLamp, an earlier project of mine that turns research papers into explainer videos. Papers have captions to anchor on. Slides don't, which is where most of the work went.&lt;/p&gt;

&lt;p&gt;A few things I learned getting a 4B model to do this well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Card layouts look like tables.&lt;/strong&gt; My first figure finder counted horizontal lines to spot tables, and decided that a slide of rounded text cards was a table, because a rounded card's top and bottom edges are lines. It now counts only stroked rules and hairline bars, and treats shapes as a diagram only when an open line actually runs from one shape to a &lt;em&gt;different&lt;/em&gt; one (a connector), not across a single card (a divider). The rest it leaves to Gemma's eyes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let the vision model be the second filter, not the first.&lt;/strong&gt; The finder errs on the side of finding too much, and Gemma throws out what isn't a figure. Asked "is this a diagram, a table, a layout or decoration?", it reliably kept the flowcharts, tables and an infinity-loop diagram, and set aside the text cards and the "Thank you" slide's social-media links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The table that ate the token budget.&lt;/strong&gt; On one big table, Gemma decided to transcribe every cell into a field I hadn't asked it to fill, ran out of tokens mid-JSON, and the figure fell back to a generic caption. The prompt now says to leave that field empty for anything that isn't a picture of text, and the parser salvages whatever fields did finish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nobody revises from paragraphs.&lt;/strong&gt; The first version wrote two or three tidy paragraphs per section, and they read like an essay. Switching to a line format (&lt;code&gt;GIST:&lt;/code&gt;, &lt;code&gt;- point&lt;/code&gt;, &lt;code&gt;STEP:&lt;/code&gt;, &lt;code&gt;TABLE:&lt;/code&gt;, &lt;code&gt;KEY:&lt;/code&gt;, &lt;code&gt;TIP:&lt;/code&gt;, &lt;code&gt;Q:&lt;/code&gt;/&lt;code&gt;A:&lt;/code&gt;) made the notes scannable, and made the model faster too: points are shorter than prose. It also streams well, because every line is complete on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be lenient with what a small model writes.&lt;/strong&gt; It sometimes drops the &lt;code&gt;TABLE:&lt;/code&gt; prefix after the header row, writes a table with only a header, or repeats a process as both points and steps. The parser treats any piped line as a table row and a lone header as a row of labels, and the prompt now asks for steps only when there are three or more, without repeating them as points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Footnotes keep it honest.&lt;/strong&gt; Because every point carries a slide tag, a wrong claim is one click away from its source. It's also why the notes stick to the slides: the prompt says to use only what's on them, and the footnotes make that checkable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The notebook is the same plain CSS and SVG as my last project, &lt;a href="https://github.com/Sachindu-Nethmin/footnotes" rel="noopener noreferrer"&gt;Footnotes&lt;/a&gt;: content flows into page-sized CSS columns, and each page turn copies the sheet into five hinged strips that curl around the spiral. New this time: the step flows (flexbox boxes with SVG pen arrows), the index-card key terms, and the subject tabs along the top edge.&lt;/p&gt;

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

&lt;p&gt;Because the slides aren't mine.&lt;/p&gt;

&lt;p&gt;Lecture slides belong to the lecturer and the university. Every cloud "summarise my slides" tool starts by asking you to upload them: someone else's material, sent to a server neither of you controls. With an open-weight model running locally, the question never comes up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The slides never leave my laptop.&lt;/strong&gt; LibreOffice, PyMuPDF and Gemma all run on the same machine. There's no account, and nothing to upload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There's no bill per page.&lt;/strong&gt; My 59-slide deck took 24 model calls (ten looks at figures, one chapter title, thirteen sections) and about seven minutes on my laptop. On a metered API that's a cost for every lecture, for every student, every semester. Here it's a few minutes of a laptop that was already on. That's what makes a one-off price possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It works without the internet.&lt;/strong&gt; Campus Wi-Fi during exam week is not something to build a product on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could fix it when it was wrong.&lt;/strong&gt; When the finder mistook cards for tables, or a table blew the token budget, I could see exactly what the model was given and change it, or swap in a different model. With no model at all, the book still fills in from the slides' own words, footnotes and all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A closed API would probably have written slightly better notes on day one. But I'd be asking students to upload their lecturers' slides to a server they don't control, and paying for it per page.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 4 E4B runs locally through Ollama and does both halves of the job. Its vision judges and captions every figure cropped from the slides (and reads pictures of text), and its text side names each chapter and writes every section of the notes.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Footnotes: a bucket-list notebook that only fills up when you go outside</title>
      <dc:creator>Sachindu Nethmin</dc:creator>
      <pubDate>Mon, 12 Oct 2026 04:59:55 +0000</pubDate>
      <link>https://dev.to/sachindu_nethmin/footnotes-a-bucket-list-notebook-that-only-fills-up-when-you-go-outside-man</link>
      <guid>https://dev.to/sachindu_nethmin/footnotes-a-bucket-list-notebook-that-only-fills-up-when-you-go-outside-man</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I have a bucket list of places I want to visit, and I wanted it to feel like a real notebook, not another app list. So I built &lt;strong&gt;Footnotes&lt;/strong&gt;: a spiral notebook on a wooden desk, with sticky-note tabs, ruled pages, taped polaroids, and pages that actually turn.&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%2Ftsbkphls057ry2q5tzoq.jpg" 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%2Ftsbkphls057ry2q5tzoq.jpg" alt="The cover of the Footnotes notebook" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does four things, and each one is meant to get me out of the chair:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. It finds beautiful places near me.&lt;/strong&gt; Waterfalls, viewpoints, peaks, temples and old forts come from open data (Wikidata), ranked by how many Wikipedias bothered to write about them, with the places closest to me always included. Around Ella, that's Ravana Falls, the Nine Arches Bridge, Ella Rock and a lot of waterfalls.&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%2F3tqhjd5g9s928rfrpbv7.jpg" 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%2F3tqhjd5g9s928rfrpbv7.jpg" alt="Nearby: beautiful places around Ella, as taped polaroids" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. It fills in each place's page.&lt;/strong&gt; When I add a place, Gemma writes its page: why to go, three things to look for when I'm standing there (a checklist I can tick on the spot), the best time, practical tips, and one surprising fact on a sticky note. The Wikipedia text it worked from is clipped onto the page like a newspaper cutting.&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%2Fynl70i9z4aqgplxsru4b.jpg" 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%2Fynl70i9z4aqgplxsru4b.jpg" alt="A place page written by Gemma" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It tells me where to go next.&lt;/strong&gt; Gemma reads my list, what I've already visited and the real 4-day forecast, picks one place, circles the best day, and gives me a small challenge that keeps the phone in my pocket. In a rainy week, it picked the sheltered Nine Arches Bridge.&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%2Fzuifpcfjw2sr16v485ie.jpg" 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%2Fzuifpcfjw2sr16v485ie.jpg" alt="Where next? Gemma's pick with the forecast" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. It turns each trip into a diary I can show someone someday.&lt;/strong&gt; When I'm actually there, I press &lt;strong&gt;I'm here!&lt;/strong&gt; and, if my phone's GPS agrees, the page gets a red passport stamp. Afterwards the margin interviews me, one question at a time, and Gemma writes the diary page in my own words, addressed to whoever I'll show it to.&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%2Fyawbmsjgpu9qlv78t3vi.jpg" 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%2Fyawbmsjgpu9qlv78t3vi.jpg" alt="The GPS passport stamp on a phone" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diary only has pages for places I've actually been. The screen is the part before I leave and after I'm home.&lt;/p&gt;

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

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The margin interviews you&lt;/th&gt;
&lt;th&gt;The diary page Gemma writes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Fq8fmc3exyhq3yve9m93e.jpg" alt="The margin interviews you about the visit" width="800" height="500"&gt;&lt;/td&gt;
&lt;td&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%2Faabqsjszbblp1swpfizu.jpg" alt="The diary page Gemma writes from your answers" width="800" height="500"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The answers in these screenshots are demo text I typed while testing. My real pages stay on my laptop.&lt;/em&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/Sachindu-Nethmin" rel="noopener noreferrer"&gt;
        Sachindu-Nethmin
      &lt;/a&gt; / &lt;a href="https://github.com/Sachindu-Nethmin/footnotes" rel="noopener noreferrer"&gt;
        footnotes
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A paper-notebook bucket list that only fills up when you go outside. Finds beautiful places near you from open data; Gemma (local, open-weight) writes the pages and your travel diary, which never leaves your device.
    &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;Footnotes&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A paper-notebook bucket list for places you want to visit. It only fills up when you go outside.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Sachindu-Nethmin/footnotes/docs/images/02-nearby.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FSachindu-Nethmin%2Ffootnotes%2FHEAD%2Fdocs%2Fimages%2F02-nearby.jpg" alt="Footnotes: places near Ella, Sri Lanka, as taped polaroids"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Footnotes looks and behaves like a spiral notebook: sticky-note tabs, ruled paper, taped polaroids, a coffee ring. The book fits your screen, laptop or phone, and pages turn instead of scrolling (next page, the dog-eared corner, the arrow keys, or a swipe). It:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Finds beautiful places near you&lt;/strong&gt; (waterfalls, viewpoints, peaks, temples, old forts) from open data, ranked by how many Wikipedias bothered to write about them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fills in each place's page.&lt;/strong&gt; An open-weight model (Gemma) writes why to go, three things to look for when you're there, the best time, practical tips, and one surprising fact. It's grounded in the Wikipedia extract, which is clipped onto the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suggests where to go next&lt;/strong&gt;, using your list, what you've already done, and the real 4-day forecast. A fixed rule keeps…&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/Sachindu-Nethmin/footnotes" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It runs locally in three commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma4:e4b
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&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;The model is Gemma 4 E4B, running on my laptop through Ollama.&lt;/strong&gt; Next.js serves the notebook, and its API routes are thin, stateless pass-throughs to &lt;code&gt;localhost:11434&lt;/code&gt;. There's also a mode where &lt;strong&gt;Gemma 2 2B runs inside the browser tab with WebLLM&lt;/strong&gt;, so the notebook works with no model server at all. Every prompt has a no-model fallback (notes straight from Wikipedia, a diary page stitched from your own answers), so the notebook never shows a blank page.&lt;/p&gt;

&lt;p&gt;Everything else is open data with no API keys: &lt;strong&gt;Wikidata&lt;/strong&gt; (a SPARQL "what's around me" query), &lt;strong&gt;Wikipedia&lt;/strong&gt; and &lt;strong&gt;Wikimedia Commons&lt;/strong&gt; (notes and photos), &lt;strong&gt;OpenStreetMap Nominatim&lt;/strong&gt; (type a town instead of sharing GPS) and &lt;strong&gt;Open-Meteo&lt;/strong&gt; (the forecast). The diary lives in the browser's IndexedDB. There's no account and no database, and &lt;em&gt;download my notebook&lt;/em&gt; gives you one JSON file.&lt;/p&gt;

&lt;p&gt;Most of the work was teaching a 4B model to behave, and a few of those lessons surprised me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The storm trek.&lt;/strong&gt; In a week of thunderstorms, Gemma suggested a mountain trek "for a dramatic contrast". So safety isn't the model's call any more: if every day in the forecast is stormy, exposed places (peaks, falls, beaches, trails) are removed from the list &lt;em&gt;before&lt;/em&gt; Gemma sees it. It still chooses and explains; it just can't send me up a mountain into lightning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rainbow.&lt;/strong&gt; The first interviewer asked my test user about "the colours in the rainbow". They'd never mentioned a rainbow. Now the interview follows a fixed plan in code (arriving, the people, the best moment, the senses, food, how it felt, advice, last words). Code decides when to move on and when a short answer deserves a follow-up. Gemma only words the question, building on what was just said. You can also skip a topic or ask for a different question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small models read the last line.&lt;/strong&gt; Even with the plan, Gemma kept asking "Describe…" questions about the mist. The topic instruction was buried in a long system prompt. Moving it to the end of the last message fixed it immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your diary, not the model's.&lt;/strong&gt; An unanswered "what was the air like?" turned into "the air was damp and warm" on the diary page. The writing prompt now labels the transcript &lt;code&gt;ME:&lt;/code&gt; and &lt;code&gt;MARGIN:&lt;/code&gt;, and a question you didn't answer is not a fact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fame isn't distance.&lt;/strong&gt; Ranking by Wikipedia fame alone let a famous national park 30 km away push Ella Rock, 1.7 km from town, off the list. A second, small-radius query now runs in parallel.&lt;/li&gt;
&lt;/ul&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%2Fma9izjl1j8fyct21zhdw.jpg" 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%2Fma9izjl1j8fyct21zhdw.jpg" alt="A page turning: five hinged strips with the ink showing through" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The notebook is plain CSS and SVG. Ruled lines are a &lt;code&gt;repeating-linear-gradient&lt;/code&gt;, the spiral is one SVG coil repeated down the spine, and tape is a &lt;code&gt;clip-path&lt;/code&gt; with ragged edges. Pages don't scroll: content flows into page-sized CSS columns, and you turn them with &lt;em&gt;next page&lt;/em&gt;, the dog-eared corner, the arrow keys or a swipe. Each turn copies the page into five hinged strips that swing around the spiral, the outer ones lifting first, so the sheet curls instead of flipping like a board. The back of each sheet shows the ink faintly bleeding through, mirrored. (One gotcha: CSS &lt;code&gt;orphans: 2&lt;/code&gt; kept pushing a whole row of polaroids onto the next page.)&lt;/p&gt;

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

&lt;p&gt;Because a diary is the most personal thing I've ever asked a model to touch.&lt;/p&gt;

&lt;p&gt;A travel diary holds who I was with, what I felt, and photos of the people I love. I want to show mine to someone years from now, not send it to a server today. With an open-weight model, that's simply the default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The words never leave my machine.&lt;/strong&gt; The interview and the diary page are written by Gemma on my laptop. With WebLLM, they never even leave the browser tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My location is blurred before anyone sees it.&lt;/strong&gt; It's rounded to about 1 km before it reaches Wikidata, OpenStreetMap or the weather service. The photos come through the notebook's own server, so Wikimedia never sees my browser at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing per page.&lt;/strong&gt; A diary is something you keep writing in for years. A metered API would turn every memory into a line on a bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can see and change how it behaves.&lt;/strong&gt; When the model got something wrong (the rainbow, the storm trek), I could look at exactly what it was given and fix it, or swap in a smaller model for an older laptop. With no model at all, the notebook still works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A closed API would have written prettier sentences on day one. But I'd be asking people to type their memories into a box that belongs to someone else, and that's not what a diary is.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 4 E4B runs locally through Ollama and writes every place page, the next-place pick, the interview and the diary page. Gemma 2 2B runs in the browser through WebLLM for a no-server mode.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>RoomPot: our room's cooking money, split by an open model</title>
      <dc:creator>Sachindu Nethmin</dc:creator>
      <pubDate>Sun, 04 Oct 2026 08:19:27 +0000</pubDate>
      <link>https://dev.to/sachindu_nethmin/roompot-our-rooms-cooking-money-split-by-an-open-model-3gim</link>
      <guid>https://dev.to/sachindu_nethmin/roompot-our-rooms-cooking-money-split-by-an-open-model-3gim</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;p&gt;&lt;em&gt;Built by me together with my roommates and teammates &lt;a class="mentioned-user" href="https://dev.to/bimsara_wickramanayaka_10"&gt;@bimsara_wickramanayaka_10&lt;/a&gt;, &lt;a class="mentioned-user" href="https://dev.to/chathuraz"&gt;@chathuraz&lt;/a&gt; and &lt;a class="mentioned-user" href="https://dev.to/ashan_lima"&gt;@ashan_lima&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fce01l6iqgsfzgskecmkb.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%2Fce01l6iqgsfzgskecmkb.png" alt="RoomPot cover" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Four of us share a room: Chathura, Bimsara, Ashan and me. We cook together, so one of us is always running to the shop for rice, eggs, onions or a new gas cylinder.&lt;/p&gt;

&lt;p&gt;Most of the time that person is &lt;strong&gt;Chathura&lt;/strong&gt;. Chathura buys whatever the room needs and pays up front. At the end of the month we're supposed to add up everything, divide the total by four, and pay back whoever spent more. We tried keeping it in an Excel sheet, but in practice receipts get lost, someone forgets to add a purchase, and the collecting just... doesn't happen. Chathura ends up quietly covering money nobody remembers to give back, and asking for it feels awkward.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;RoomPot&lt;/strong&gt; for Chathura and for the rest of us. It's a small group chat for the room. Whoever buys something just says it, the way you'd text a friend:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;chicken 1.2k, onions 300, tomatoes 250&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;RoomPot reads the message with an open-weight AI model (Google's &lt;strong&gt;Gemma&lt;/strong&gt;). It knows who is logged in, so it records that person as the payer. It splits the cost between the four of us and keeps a running tally of who owes whom. Nobody has to remember anything or do maths at the end of the month.&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%2Ffte8nuzptaxtb25j1up3.jpg" 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%2Ffte8nuzptaxtb25j1up3.jpg" alt="Chathura logs a purchase and RoomPot splits it four ways" width="750" height="1624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log costs by chatting.&lt;/strong&gt; "rice 1200, eggs 450" works, and so do "Chathura bought bread 200" (credits Chathura) and "shampoo for me and Ashan 800" (splits between just those two).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Receipt photos.&lt;/strong&gt; Snap the shop bill and every item is logged. Totals, cash and change lines are ignored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average daily spend.&lt;/strong&gt; The number we actually wanted to know: what the room spends per day and per person, plus today's spend, this month, the last 7 days, and a 30-day chart.&lt;/li&gt;
&lt;/ul&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%2Fuxheugwgff4hixyptqgw.jpg" 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%2Fuxheugwgff4hixyptqgw.jpg" alt="Average daily spend" width="750" height="1624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Settle up.&lt;/strong&gt; It works out the fewest payments that clear everyone's debts, with a &lt;em&gt;Mark paid&lt;/em&gt; button. You can also just type "gave Chathura 1500".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask it anything about our money.&lt;/strong&gt; "What's our average daily spend and who still owes me?" is answered from our own data.&lt;/li&gt;
&lt;/ul&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%2Fdltugb36108n7n2frvo4.jpg" 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%2Fdltugb36108n7n2frvo4.jpg" alt="The Money tab: average daily spend, 30-day chart and settle-up" width="750" height="1624"&gt;&lt;/a&gt;&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://roompot.onrender.com" rel="noopener noreferrer"&gt;https://roompot.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sign up with any username, create a room, and try messages like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rice 1200, eggs 450, gas 3800&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shampoo for me and &amp;lt;a roommate's name&amp;gt; 800&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;what's our average daily spend?&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also invite a second account with the room's invite code to see the splitting and settle-up. &lt;em&gt;(It's on Render's free plan, so if it has been idle, the first load takes about a minute to wake up.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img alt="Asking RoomPot a question about our spending"&gt;&lt;/a&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/Sachindu-Nethmin" rel="noopener noreferrer"&gt;
        Sachindu-Nethmin
      &lt;/a&gt; / &lt;a href="https://github.com/Sachindu-Nethmin/roompot" rel="noopener noreferrer"&gt;
        roompot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Shared room budget for roommates who cook together. Chat what you bought; Gemma (open-weight, via Ollama) splits it and tracks who owes whom.
    &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;🍳 RoomPot&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A shared room budget for roommates who cook together.&lt;/strong&gt; Tell the group chat what you bought, like &lt;em&gt;"rice 1200, eggs 450, gas 3800"&lt;/em&gt;, or send a photo of the bill. An open-weight model (Gemma) reads it. It runs on a server by default so it works in any browser, and anyone can switch on &lt;strong&gt;private mode&lt;/strong&gt; to run it inside their own browser instead. It logs the cost under whoever is logged in, splits it between the room, and keeps a running tally of who owes whom. No more "machan, did I give you money for the gas last week?"&lt;/p&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Sachindu-Nethmin/roompot/docs/chat.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FSachindu-Nethmin%2Froompot%2FHEAD%2Fdocs%2Fchat.jpg" width="360" alt="Chat where roommates log purchases and RoomPot splits them"&gt;&lt;/a&gt;
  &amp;nbsp;
  &lt;a rel="noopener noreferrer" href="https://github.com/Sachindu-Nethmin/roompot/docs/money.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FSachindu-Nethmin%2Froompot%2FHEAD%2Fdocs%2Fmoney.jpg" width="360" alt="Money dashboard with average daily spend, 30-day chart and settle-up"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why it exists&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Four of us share a room and cook together. Whoever goes to the shop pays, and at the end of the month we add it all up and divide by four. Except someone always forgets to write a purchase down, and someone else forgets…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Sachindu-Nethmin/roompot" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


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

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Next.js (App Router), MongoDB Atlas with Mongoose, and Tailwind CSS, deployed on Render.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma is the core.&lt;/strong&gt; Every chat message goes to Gemma along with the room's member names and a short set of rules. Gemma returns structured JSON:&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;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expense"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chicken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"amount"&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="w"&gt; &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;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&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;span class="nl"&gt;"paidBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"splitWith"&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;span class="nl"&gt;"settleTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"settleAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;p&gt;The intent can be an expense, a payment back to someone, a question, or just chat. From that, the app records the purchase, records the payment, or answers the question using the room's real numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model is never trusted blindly.&lt;/strong&gt; Every response is checked with a zod schema. Amounts must be positive and sensible, and names must match real room members. A bad or missing answer falls back to a simple rule-based parser, so the room never stops working. The money maths (balances, the fewest settle-up payments, daily averages in our timezone) is plain, exact code. The AI only does what it's good at: turning messy human text and photos into clean data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same open model runs in three places&lt;/strong&gt;, and switching between them is one setting:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Used for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Render + Google AI Studio&lt;/td&gt;
&lt;td&gt;Gemma 4 (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The live app. Works in any browser and reads receipt photos.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;My laptop, through Ollama&lt;/td&gt;
&lt;td&gt;Gemma 4 E4B&lt;/td&gt;
&lt;td&gt;Building and testing, fully offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inside the phone's browser&lt;/td&gt;
&lt;td&gt;Gemma 2 2B through WebLLM (WebGPU)&lt;/td&gt;
&lt;td&gt;Optional &lt;strong&gt;private mode&lt;/strong&gt;: after a one-time download, messages and photos never leave the phone. Receipts are read on-device with Tesseract.js.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before shipping I ran a set of test messages through Gemma: purchases, "someone else paid", partial splits, paying someone back, questions, and a purchase with no price. All 13 were handled correctly, and a printed receipt was read line by line with the total, cash and change ignored.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Our money is personal.&lt;/strong&gt; Who bought what and who owes whom is our business. Because Gemma's weights are open, the same model family can run &lt;em&gt;inside a phone's browser&lt;/em&gt;, so private mode keeps every message on the device. With a closed API, the only option would be sending everything to someone else's server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; Four roommates splitting cooking costs are not going to pay for an AI subscription. Ollama on a laptop is free, the in-browser model is free, and Gemma on Google AI Studio's free tier is free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It works offline.&lt;/strong&gt; On my laptop with Ollama, RoomPot keeps working with no internet at all. That's how I built and tested most of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lock-in.&lt;/strong&gt; The model is one environment variable. Moving from Gemma 3 to Gemma 4 was a one-line change, and the same code also works with any OpenAI-compatible open-model server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be honest about the trade-off: in the hosted version, messages are sent to Google AI Studio to be read by Gemma. Private mode exists for anyone in the room who doesn't want that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What My Roommates Said
&lt;/h2&gt;

&lt;p&gt;I sent the link to the room. Bimsara tried it first and messaged me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Machan, just tested it. It works perfectly, way better than our messy Excel sheet."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(&lt;em&gt;Machan&lt;/em&gt; is Sri Lankan slang for "buddy".)&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 4 powers the live app and my offline setup, and Gemma 2 2B runs in the browser for private mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; the app is deployed as a Render web service, built straight from the GitHub repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of MongoDB Atlas:&lt;/strong&gt; Atlas is the data layer for users, rooms, purchases, payments and the chat history behind the open-weight model.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I built RoomPot with help from an AI coding assistant. The idea, the problem, the decisions, and testing it with my roommates are ours.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>My Academic Journey at Sabaragamuwa University of Sri Lanka</title>
      <dc:creator>Sachindu Nethmin</dc:creator>
      <pubDate>Wed, 28 May 2025 15:36:20 +0000</pubDate>
      <link>https://dev.to/sachindu_nethmin/my-academic-journey-at-sabaragamuwa-university-of-sri-lanka-3fom</link>
      <guid>https://dev.to/sachindu_nethmin/my-academic-journey-at-sabaragamuwa-university-of-sri-lanka-3fom</guid>
      <description>&lt;p&gt;What I Experienced in My Studies at Sabaragamuwa University&lt;/p&gt;

&lt;p&gt;My heart was full of joy, curiosity and some fear when I opened the gate at &lt;a href="https://www.sab.ac.lk/" rel="noopener noreferrer"&gt;Sabaragamuwa University of Sri Lanka&lt;/a&gt;. I understood from my study in the BSc (Hons) in Computing and Information Systems that I was getting ready for a time in life that would influence both my learning and career goals.&lt;/p&gt;

&lt;p&gt;The use of Technology as a Firm Support&lt;/p&gt;

&lt;p&gt;The first year built the essential basis for what was to come. Since the beginning, the CIS program showed me the fundamentals of programming, computer systems and problem fixing. When I wrote and tested my first program as a student, I still remember the thrill of seeing it work successfully in the lab. With experience, I have learned about software engineering, database systems and computer networks necessary components needed to be a professional in IT.&lt;/p&gt;

&lt;p&gt;What stands out to me most in the program is how it blends learning about science with actual hands-on activities. Every time I go to a lecture, I can become involved in lab sessions that teach me with real-life activities. Through my partnership in these sessions, I have gained better critical thinking and technical abilities.&lt;/p&gt;

&lt;p&gt;A Culture of Collaboration and Innovation&lt;/p&gt;

&lt;p&gt;At Sabaragamuwa University, education extends well outside the classroom. I have had the opportunity to be part of group projects, coding competition, and student initiated projects. This has provided me with lessons on teamwork, time management, critical thinking and finding creative ways to solve difficult problems.&lt;/p&gt;

&lt;p&gt;Open-source contribution and community participation are strongly emphasized here, and I have already participated in several exciting opportunities that make the classroom learning as well as the goals out there very tangible. From contributing to a GitHub project, to brainstorming during hackathons, I’ve found limitless ways to stay motivated and involved.&lt;/p&gt;

&lt;p&gt;Mentorship That Matters&lt;/p&gt;

&lt;p&gt;The best part of my academic experience has been the mentorship from our lecturers and mentors. They are not just professionals in their jobs but truly invested in our success. Their advice has inspired me to venture beyond the syllabus, reach out in independent study, and even look ahead towards research experiences.&lt;/p&gt;

&lt;p&gt;Their never ending support just helping me to be on the right track. I’m interested in machine learning, cybersecurity, and software development, among those areas, I want also to find some internships and side-projects in order to level up my skill-sets.&lt;/p&gt;

&lt;p&gt;Looking Ahead&lt;/p&gt;

&lt;p&gt;I am in my second year and feeling more confident and determined than ever. The path has not been easy, but each obstacle has served as a steppingstone in becoming more hardened and clear-minded. I think if I continue trying and remain curious and passionate I may do great thinks in technology.&lt;/p&gt;

&lt;p&gt;It is not just a place of lectures to me, it is a place where I found myself and who I want to be. Blockquote Sabaragamuwa University of Sri Lanka.&lt;/p&gt;

&lt;p&gt;Thanks for reading! If you are a student, a comp-sci enthusiast or just want to say hi, you can reach out and share your academic experience with me too. Let’s motivate and develop each other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i21pkw5i2ree2miwrbb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i21pkw5i2ree2miwrbb.jpg" alt=" " width="800" height="966"&gt;&lt;/a&gt;&lt;/p&gt;

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