<?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: Gabriel axe</title>
    <description>The latest articles on DEV Community by Gabriel axe (@baron_ga).</description>
    <link>https://dev.to/baron_ga</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%2F4169903%2F72c28bf6-7f06-4e57-8622-82e40d78f0c1.jpg</url>
      <title>DEV Community: Gabriel axe</title>
      <link>https://dev.to/baron_ga</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/baron_ga"/>
    <language>en</language>
    <item>
      <title>Grassling</title>
      <dc:creator>Gabriel axe</dc:creator>
      <pubDate>Mon, 12 Oct 2026 00:30:38 +0000</pubDate>
      <link>https://dev.to/baron_ga/grassling-1naj</link>
      <guid>https://dev.to/baron_ga/grassling-1naj</guid>
      <description>&lt;h1&gt;
  
  
  Grassling: An AI Pet That Actually Wants You to Leave Your Screen
&lt;/h1&gt;

&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 built &lt;strong&gt;Grassling&lt;/strong&gt;, a small Android virtual pet with one unusual goal:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;it wants you to put your phone down and go outside.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I discovered this Hacktoberfest challenge a little later than I would have liked, so Grassling was built under a pretty tight time constraint.&lt;/p&gt;

&lt;p&gt;That forced me to focus on the core idea first: get a real Android app working, track movement, build a simple game loop, and run a local language model directly on the phone.&lt;/p&gt;

&lt;p&gt;There are still rough edges, especially around dialogue quality and polish, but the core system works and gave me a chance to experiment with something I had wanted to try for a while: &lt;strong&gt;on-device AI on Android&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The idea started from the "Touch Grass" theme itself.&lt;/p&gt;

&lt;p&gt;Instead of building another AI application designed to keep someone chatting with a screen, I wanted the AI to actively encourage the opposite behavior.&lt;/p&gt;

&lt;p&gt;Grassling has an energy level that slowly increases over time.&lt;/p&gt;

&lt;p&gt;As that energy rises, its mood changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sleeping&lt;/li&gt;
&lt;li&gt;Normal&lt;/li&gt;
&lt;li&gt;Anxious&lt;/li&gt;
&lt;li&gt;Exploding with rage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main way to calm it down is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;walk.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The application tracks the user's steps using Android's step counter. Walking reduces Grassling's energy, while completing outdoor quests reduces it even further.&lt;/p&gt;

&lt;p&gt;Some current quests are things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Walk 750 steps&lt;/li&gt;
&lt;li&gt;Take a 10-minute walk&lt;/li&gt;
&lt;li&gt;Find something blue outside&lt;/li&gt;
&lt;li&gt;Find three different plants&lt;/li&gt;
&lt;li&gt;Spend five minutes outdoors&lt;/li&gt;
&lt;li&gt;Walk 500 steps without using your phone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is intentionally not much to do by staring at the app.&lt;/p&gt;

&lt;p&gt;You open it, check on Grassling, accept a quest...&lt;/p&gt;

&lt;p&gt;...and then the useful thing to do is leave.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the AI comes in
&lt;/h3&gt;

&lt;p&gt;Grassling runs &lt;strong&gt;Gemma 3 270M locally on the Android device&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Gemma gives the character its personality.&lt;/p&gt;

&lt;p&gt;It generates short dialogue based on Grassling's current state and reacts when the user accepts a quest.&lt;/p&gt;

&lt;p&gt;An anxious Grassling should not sound like a sleeping Grassling, and an extremely energetic Grassling should feel noticeably more desperate to get outside.&lt;/p&gt;

&lt;p&gt;One important detail is that &lt;strong&gt;Gemma currently does not generate the quests themselves&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The quests come from a predefined pool and are randomly selected.&lt;/p&gt;

&lt;p&gt;I deliberately kept the game rules deterministic while using the model for the part where generative AI is actually useful: personality and variation.&lt;/p&gt;

&lt;p&gt;That separation also makes the application much easier to reason about.&lt;/p&gt;

&lt;p&gt;The AI can say something weird.&lt;/p&gt;

&lt;p&gt;It cannot randomly give itself 500 energy points.&lt;/p&gt;

&lt;p&gt;Because I was working with limited time, this separation also helped a lot. I could focus on making the game loop reliable without depending on the language model for critical application behavior.&lt;/p&gt;

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

&lt;p&gt;The project is open source:&lt;/p&gt;

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

&lt;p&gt;The Android application is primarily built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kotlin&lt;/li&gt;
&lt;li&gt;Jetpack Compose&lt;/li&gt;
&lt;li&gt;Android step-counter sensors&lt;/li&gt;
&lt;li&gt;Foreground Services&lt;/li&gt;
&lt;li&gt;WorkManager&lt;/li&gt;
&lt;li&gt;Kotlin Coroutines / Flow&lt;/li&gt;
&lt;li&gt;C++17&lt;/li&gt;
&lt;li&gt;JNI&lt;/li&gt;
&lt;li&gt;llama.cpp&lt;/li&gt;
&lt;li&gt;Gemma 3 270M IT Q8_0 / GGUF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application supports &lt;code&gt;arm64-v8a&lt;/code&gt; and &lt;code&gt;x86_64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One architectural decision I particularly liked was keeping the game logic separate from the language model.&lt;/p&gt;

&lt;p&gt;Conceptually, the flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Android UI
    │
    ├── Step Counter
    │       │
    │       └── Energy / Mood
    │
    ├── Quest System
    │
    └── Local AI
            │
            └── JNI
                 │
                 └── llama.cpp
                      │
                      └── Gemma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemma receives information about the current state.&lt;/p&gt;

&lt;p&gt;It generates dialogue.&lt;/p&gt;

&lt;p&gt;But it does not control the state itself.&lt;/p&gt;

&lt;p&gt;That means energy calculation, walking progress, quest completion, and persistence remain deterministic Kotlin code.&lt;/p&gt;

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

&lt;p&gt;The application started with the basic virtual-pet loop.&lt;/p&gt;

&lt;p&gt;Grassling has an energy value from 0 to 100.&lt;/p&gt;

&lt;p&gt;The value increases automatically over time, reaching maximum energy in roughly 24 hours if the user does nothing.&lt;/p&gt;

&lt;p&gt;Walking decreases it.&lt;/p&gt;

&lt;p&gt;Currently, every 11 new steps remove one energy point, while completing a quest provides an additional energy reduction.&lt;/p&gt;

&lt;p&gt;From there, I added Android's &lt;code&gt;TYPE_STEP_COUNTER&lt;/code&gt; sensor and a foreground service so the app could keep tracking walking without requiring the main screen to remain open.&lt;/p&gt;

&lt;p&gt;Then came the part I was most curious about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;running the language model directly on Android.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since I discovered the challenge late, I did not have much time to slowly prototype each part.&lt;/p&gt;

&lt;p&gt;A lot of the project became a process of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;get the smallest version working&lt;/li&gt;
&lt;li&gt;test it on a real phone&lt;/li&gt;
&lt;li&gt;find what breaks&lt;/li&gt;
&lt;li&gt;fix it&lt;/li&gt;
&lt;li&gt;move to the next part&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That made the project a little rough around the edges, but it was also a very practical way to learn.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemma + llama.cpp
&lt;/h3&gt;

&lt;p&gt;I used a quantized &lt;strong&gt;Gemma 3 270M IT Q8_0&lt;/strong&gt; model in GGUF format.&lt;/p&gt;

&lt;p&gt;Instead of calling a cloud endpoint, Grassling talks to &lt;code&gt;llama.cpp&lt;/code&gt; through JNI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kotlin
  ↓
JNI
  ↓
C++
  ↓
llama.cpp
  ↓
Gemma GGUF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is not included inside the APK.&lt;/p&gt;

&lt;p&gt;On first launch, the app downloads it using WorkManager into the application's private storage.&lt;/p&gt;

&lt;p&gt;The current model is around 278 MiB.&lt;/p&gt;

&lt;p&gt;After downloading it, Grassling validates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expected file size&lt;/li&gt;
&lt;li&gt;GGUF header&lt;/li&gt;
&lt;li&gt;SHA-256 checksum&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only after validation is the model loaded.&lt;/p&gt;

&lt;p&gt;Once that initial download has completed, inference itself happens locally.&lt;/p&gt;

&lt;p&gt;No OpenAI API.&lt;/p&gt;

&lt;p&gt;No Gemini API.&lt;/p&gt;

&lt;p&gt;No server running the model somewhere else.&lt;/p&gt;

&lt;p&gt;The phone is actually doing the inference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Gemma on a real phone
&lt;/h3&gt;

&lt;p&gt;I tested the local inference directly on a Samsung Galaxy A54.&lt;/p&gt;

&lt;p&gt;For the current quantized model, I measured approximately:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time to first token&lt;/td&gt;
&lt;td&gt;~212 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt processing&lt;/td&gt;
&lt;td&gt;~222 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token generation&lt;/td&gt;
&lt;td&gt;~27.5 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native model loading&lt;/td&gt;
&lt;td&gt;~759 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That performance was much better than I originally expected from local inference on a mid-range Android phone.&lt;/p&gt;

&lt;p&gt;The harder problem turned out not to be speed.&lt;/p&gt;

&lt;p&gt;It was &lt;strong&gt;quality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I ran 20 simulated quest-acceptance interactions across the different moods.&lt;/p&gt;

&lt;p&gt;Every request returned a response, but the quality was inconsistent.&lt;/p&gt;

&lt;p&gt;Some responses reflected the mood well.&lt;/p&gt;

&lt;p&gt;Others were generic, repeated part of the quest, or produced something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Please allow me to proceed."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That taught me something useful very quickly:&lt;/p&gt;

&lt;p&gt;getting a model to run locally is one problem.&lt;/p&gt;

&lt;p&gt;Getting a tiny model to consistently produce good character dialogue is another.&lt;/p&gt;

&lt;p&gt;Given more time, this is probably the part I would iterate on the most.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working with a tiny model
&lt;/h3&gt;

&lt;p&gt;Using a 270M-parameter model forced me to think differently about prompting.&lt;/p&gt;

&lt;p&gt;I cannot simply throw a huge system prompt at it and expect a perfect character.&lt;/p&gt;

&lt;p&gt;The prompts have to be small and focused.&lt;/p&gt;

&lt;p&gt;Grassling is given information such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mood: ANXIOUS
Energy: 75
Quest accepted: Walk 750 steps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and asked for a very short reaction.&lt;/p&gt;

&lt;p&gt;Generation is intentionally capped at a small number of tokens.&lt;/p&gt;

&lt;p&gt;That helped latency, but it also exposed the limitations of the model.&lt;/p&gt;

&lt;p&gt;Prompt wording has a surprisingly large effect on whether the result feels like a character reaction or just a paraphrase of the quest.&lt;/p&gt;

&lt;p&gt;That became one of the most interesting parts of the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  The screen should be the shortest part
&lt;/h3&gt;

&lt;p&gt;A lot of software optimizes for engagement.&lt;/p&gt;

&lt;p&gt;More screen time.&lt;/p&gt;

&lt;p&gt;More notifications.&lt;/p&gt;

&lt;p&gt;More things to tap.&lt;/p&gt;

&lt;p&gt;For this challenge I wanted to experiment with the inverse.&lt;/p&gt;

&lt;p&gt;Grassling is useful precisely when you &lt;strong&gt;stop interacting with Grassling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Accept the mission.&lt;/p&gt;

&lt;p&gt;Put the phone in your pocket.&lt;/p&gt;

&lt;p&gt;Go walk.&lt;/p&gt;

&lt;p&gt;Come back later.&lt;/p&gt;

&lt;p&gt;For once, "user engagement" means getting the user away from the UI.&lt;/p&gt;

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

&lt;p&gt;Local AI is not just a technical detail in Grassling.&lt;/p&gt;

&lt;p&gt;It changes what the application can be.&lt;/p&gt;

&lt;p&gt;With a traditional closed AI API, every interaction would look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phone
  ↓
Internet
  ↓
Company API
  ↓
Model
  ↓
Internet
  ↓
Phone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That creates several problems for this particular idea.&lt;/p&gt;

&lt;p&gt;First, Grassling is supposed to encourage outdoor activity.&lt;/p&gt;

&lt;p&gt;Outdoor activity is exactly where connectivity can become unreliable.&lt;/p&gt;

&lt;p&gt;A character that suddenly loses its personality because there is no signal would defeat part of the idea.&lt;/p&gt;

&lt;p&gt;With Gemma and llama.cpp, the important path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phone
  ↓
Gemma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the initial model download, inference does not depend on a remote AI service.&lt;/p&gt;

&lt;p&gt;There is also no per-message API cost.&lt;/p&gt;

&lt;p&gt;And the user's game state does not need to be sent to a third-party inference server just to generate a sentence from a virtual pet.&lt;/p&gt;

&lt;p&gt;But the biggest advantage for me as a developer was experimentation.&lt;/p&gt;

&lt;p&gt;I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change the prompt&lt;/li&gt;
&lt;li&gt;change inference parameters&lt;/li&gt;
&lt;li&gt;inspect performance&lt;/li&gt;
&lt;li&gt;replace the quantization&lt;/li&gt;
&lt;li&gt;try another compatible model&lt;/li&gt;
&lt;li&gt;benchmark everything locally&lt;/li&gt;
&lt;li&gt;inspect llama.cpp&lt;/li&gt;
&lt;li&gt;change the native integration&lt;/li&gt;
&lt;li&gt;understand exactly where the model is running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model is not a black box behind an HTTP endpoint.&lt;/p&gt;

&lt;p&gt;I am still learning how local inference works, and being able to inspect and modify the entire stack made this project much more interesting than simply calling an API.&lt;/p&gt;

&lt;p&gt;The short deadline actually made this even more noticeable.&lt;/p&gt;

&lt;p&gt;Because the whole stack was accessible, I could make changes quickly, test them directly on the device, and understand what was happening when something did not work.&lt;/p&gt;

&lt;p&gt;Open innovation made it possible for someone building an Android project at home to put a real language model inside a phone and experiment with the whole pipeline.&lt;/p&gt;

&lt;p&gt;That is pretty amazing.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Best Use of Gemma
&lt;/h3&gt;

&lt;p&gt;Grassling uses &lt;strong&gt;Gemma 3 270M IT Q8_0&lt;/strong&gt; as the local language model responsible for the virtual pet's dialogue and quest-acceptance reactions.&lt;/p&gt;

&lt;p&gt;Gemma runs directly on Android through &lt;code&gt;llama.cpp&lt;/code&gt; and JNI rather than through a hosted model API.&lt;/p&gt;

&lt;p&gt;The project also includes real-device benchmarks and experiments around the strengths and limitations of running a small Gemma model locally.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
