<?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: Mark Cantimbuhan</title>
    <description>The latest articles on DEV Community by Mark Cantimbuhan (@justcallmemarko).</description>
    <link>https://dev.to/justcallmemarko</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%2F4155272%2Fc7ae9885-4cf3-476d-96df-c88d3817f50b.jpg</url>
      <title>DEV Community: Mark Cantimbuhan</title>
      <link>https://dev.to/justcallmemarko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/justcallmemarko"/>
    <language>en</language>
    <item>
      <title>GPlay: turning dead phone time into playground time with local Gemma</title>
      <dc:creator>Mark Cantimbuhan</dc:creator>
      <pubDate>Sat, 10 Oct 2026 17:37:45 +0000</pubDate>
      <link>https://dev.to/justcallmemarko/gplay-turning-dead-phone-time-into-playground-time-with-local-gemma-142f</link>
      <guid>https://dev.to/justcallmemarko/gplay-turning-dead-phone-time-into-playground-time-with-local-gemma-142f</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;&lt;strong&gt;GPlay&lt;/strong&gt; answers one question fast: &lt;em&gt;"We've got five people, a ball, and a patch of grass — what can we actually play, right now?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You tell it how many people you have, what's in your bag, and where you are. It filters a library of real-world games down to the handful that genuinely fit those constraints, then uses &lt;strong&gt;Gemma (open weights, running locally through Ollama)&lt;/strong&gt; to write a short, tailored playbook — setup, how to play, how to win — adapted to &lt;em&gt;your&lt;/em&gt; group size, equipment, and space.&lt;/p&gt;

&lt;p&gt;The goal is to make the screen the shortest part of the experience. Open GPlay, get a pick, read three bullets, walk outside.&lt;/p&gt;

&lt;p&gt;It has three views:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Home / Coach&lt;/strong&gt; — describe your group ("4 kids and 2 adults, one frisbee, small yard") and it suggests playable game cards you can tap for rules. Ask it something unrelated and it politely refuses and steers back to games.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Games Catalog&lt;/strong&gt; — the full local library with search and area filters. Every card opens streamed, adapted rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoreboard&lt;/strong&gt; — pick any game and log scores, with a ranked table per game.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Run it yourself in two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# local Ollama: ollama serve &amp;amp;&amp;amp; ollama pull gemma4:latest&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;then open &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;http://localhost:5173&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/JustCallMeMarko" rel="noopener noreferrer"&gt;
        JustCallMeMarko
      &lt;/a&gt; / &lt;a href="https://github.com/JustCallMeMarko/GPlay" rel="noopener noreferrer"&gt;
        GPlay
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;GPlay&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;An open-source, local-first web app for the Hacktoberfest &lt;strong&gt;"Touch Grass"&lt;/strong&gt; challenge
Tell it how many people you have, what equipment is on hand, and where you are — it
instantly filters matching physical games and uses Google's open-weight &lt;strong&gt;Gemma&lt;/strong&gt; model
(via Ollama) to stream tailored rules, setups, and adaptations as Markdown over SSE.&lt;/p&gt;
&lt;p&gt;Zero cloud APIs, zero API fees, works fully offline.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Architecture&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;[ React + Tailwind ] --REST/SSE--&amp;gt; [ FastAPI ] --filter--&amp;gt; [ SQLite games DB ]
                                          \
                                           '--local HTTP--&amp;gt; [ Ollama: gemma4:latest ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Docker&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Uses Ollama installed on your machine (ollama serve + ollama pull gemma4:latest)&lt;/span&gt;
docker compose up --build

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; No local Ollama? Run everything in containers instead:&lt;/span&gt;
docker compose --profile containerized-ollama up --build&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Serves the frontend at &lt;a href="http://localhost:5173" rel="nofollow noopener noreferrer"&gt;http://localhost:5173&lt;/a&gt;. &lt;code&gt;OLLAMA_URL&lt;/code&gt;/&lt;code&gt;GPLAY_MODEL&lt;/code&gt;
env vars override the defaults (&lt;code&gt;http://host.docker.internal:11434&lt;/code&gt; / &lt;code&gt;gemma4:latest&lt;/code&gt;).&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Manual&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;1. Backend&lt;/h4&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; server
uv venv &lt;span class="pl-k"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv pip&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/JustCallMeMarko/GPlay" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; React 19 + Vite + Tailwind CSS on the front end, FastAPI on the back end, SQLite for the seeded 29-game catalog, and &lt;strong&gt;Ollama serving &lt;code&gt;gemma4:latest&lt;/code&gt;&lt;/strong&gt; for every bit of language generation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture:&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;[ React + Tailwind ] --REST/SSE--&amp;gt; [ FastAPI ] --filter--&amp;gt; [ SQLite games DB ]
                                       \
                                        '--local HTTP--&amp;gt; [ Ollama: gemma4:latest ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few decisions worth calling out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model only does what it is good at.&lt;/strong&gt; "Which of these 29 games fit 4 people with one frisbee on grass?" is a deterministic filter — so it is plain Python/SQL, not a prompt. Gemma is handed only the games that &lt;em&gt;already match&lt;/em&gt; and asked to adapt them. That keeps results fast, predictable, and cheap, and it means a small local model behaves well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything streams.&lt;/strong&gt; Rules and chat are streamed token-by-token from FastAPI as Server-Sent Events and rendered as Markdown with &lt;code&gt;react-markdown&lt;/code&gt;, so the playbook starts appearing immediately instead of after a long wait.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The chat markers never leak.&lt;/strong&gt; The Coach prompt is restricted to physical games only. When it wants to show game cards it emits a &lt;code&gt;GAMES_PICK: id,id,id&lt;/code&gt; line, which the backend strips out line-by-line mid-stream and converts into a structured &lt;code&gt;{"games": [...]}&lt;/code&gt; event. The user sees prose plus cards; the plumbing stays invisible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model can invent new games — under constraints.&lt;/strong&gt; A separate endpoint has Gemma return strict JSON for a brand-new game, then validates it before storing: equipment and areas must be from the allowed sets, player counts are clamped, and the slug is de-duplicated. Bad model output is rejected, not saved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The AI is optional by design.&lt;/strong&gt; If Ollama or the model isn't running, &lt;code&gt;/api/rules&lt;/code&gt; and &lt;code&gt;/api/chat&lt;/code&gt; fall back to deterministic, built-in rules, streamed with the same token-by-token behaviour. The app works end-to-end offline — the AI is an upgrade, not a dependency.&lt;/p&gt;

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

&lt;p&gt;This is an app you're supposed to use &lt;strong&gt;outside&lt;/strong&gt; — in a yard, a park, a campsite, the backcountry. That makes the open approach the whole point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It runs with no internet.&lt;/strong&gt; Inference happens on the machine in front of you. No signal required, no round-trip to a server that might be slow or down. A bird-call-style "works on the trail with no bars" promise, applied to play.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No data leaves your machine.&lt;/strong&gt; A group's location, ages, and habits stay local. There is no vendor seeing which games a school picked or where a run club meets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No API keys, no per-request cost.&lt;/strong&gt; Open weights mean the app costs nothing to run. A teacher, a parent, or a run-club organiser can clone it and use it without a billing account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model is swappable.&lt;/strong&gt; &lt;code&gt;GPLAY_MODEL&lt;/code&gt; is an env var. Point it at a different Ollama model, a fine-tune, or a smaller model for a weak laptop — nothing else changes. A closed API would lock both the model and the terms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where open beat closed here: GPlay works on a phone in a backyard with no cell service, adapts everything to a group's real situation without sending that situation anywhere, and stays free forever to the people who'd benefit most from it.&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; — the project's language generation is Gemma via local Ollama: adapted rules, the Coach chat, and constrained new-game generation all run through it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Team
&lt;/h2&gt;

&lt;p&gt;Solo — &lt;a href="https://dev.to/justcallmemarko"&gt;@justcallmemarko&lt;/a&gt;.&lt;/p&gt;

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