DEV Community

Cover image for Step Out: An Offline AI Break Planner with Gemma and Ollama
cynthia wahome
cynthia wahome

Posted on

Step Out: An Offline AI Break Planner with Gemma and Ollama

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the MLH x DEV Writing Challenge.

What I Built

“Touch grass” is excellent advice until you look outside and find concrete, parked cars, and a neighbour reversing with considerable confidence.

For someone juggling jobs and spending most of the day at a screen, an outdoor break needs to fit ordinary life. There might be ten spare minutes and a familiar courtyard. A scenic hiking trail is unlikely to appear between two meetings.

I built Step Out, a local AI outdoor break planner that turns a few preferences into one short activity. It uses Gemma 3 1B through Ollama to generate plans on the computer running the app, without a cloud inference API.

The idea is straightforward: spend a moment planning, then leave the screen behind.

Four questions and an exit strategy

The user chooses:

  1. Available time: 5, 10, 15, or 20 minutes.
  2. A familiar outdoor space: a compound or courtyard, a familiar street, or an outdoor seating area.
  3. Their goal: a quiet pause, gentle movement, or noticing their surroundings.
  4. Their mobility preference: walking or standing, or a seated activity.

Step Out returns an activity card with instructions, a schedule and a reflection prompt. The user can save it in the browser or download step-out-activity.txt to use away from the app.

There is no account to create, no exact GPS location to surrender, and no streak waiting to judge you tomorrow.

Demo

Repository: CynthiaWahome/step-out

Video walkthrough: Watch Step Out

The current walkthrough shows the preference selections, the generation request and the error feedback when that request does not complete. It does not show the successful generation or offline tests described below.

That distinction matters. A loading button is evidence of a loading button, not a completed AI plan.

Local inference on an Apple M1

In my tests, Gemma 3 1B ran locally on an Apple M1 laptop. A cold request took 7.45 seconds, while warm requests ranged from 5.45 to 7.54 seconds.

These are measurements from my setup, not a promise that every laptop will perform identically. Local inference depends on hardware, memory and what else the computer is doing. A laptop with 47 browser tabs may have its own opinions.

I also tested fresh generation with Wi-Fi switched off and no other internet connection active. After the model and application dependencies were installed, the local app could still generate a new plan.

The model runs on the laptop. This version does not run it on a phone.

Taking the plan outside

On 9th October 2026, I tried a ten-minute seated activity in my residential compound courtyard. I chose a quiet pause, and the generated activity was “Notice outdoor sounds.”

The schedule was:

Phase Time
Get ready and reach the outdoor spot 1 minute
Observe the sounds around me 8 minutes
Return and settle in 1 minute

The instruction asked me to notice three sounds without using my phone and remain seated throughout.

The break took 9 minutes and 30 seconds. I checked my screen zero times. I listened to birds and the neighbourhood hum from a stone bench, then returned and saved my reflection in the browser.

It was a small break, but it gave the project a useful reality check: could the instructions make sense once I stopped looking at the interface?

Partner Technologies

Gemma and Ollama for local AI inference

I used Google's open-weight Gemma 3 1B, with the model tag gemma3:1b, served locally by Ollama.

The Next.js backend sends generation requests to the local Ollama service at 127.0.0.1:11434. In this laptop setup, the preferences are processed on the same computer, and reflections stay in that browser's local storage.

There are no paid inference API token charges. There are still hardware and electricity costs, and the computer has finite capacity. “Local” does not mean “powered by wishes.”

The demo also handles one generation at a time. Avoiding an external provider's usage quota does not remove the application's own capacity limits.

Code handles the clock

I kept time calculations out of the model's job description.

The backend allocates one minute for preparation, one or three minutes for returning, and the remaining time to the activity. Those phases add up to the user's selected duration.

Gemma selects and adapts an activity. It does not decide that a ten-minute break should somehow contain fourteen minutes of walking.

Before inference, the backend filters a curated collection of five activities by the selected space, goal and mobility preference. A seated choice excludes the walking activity from the eligible options.

These rules narrow the model's choices. They do not verify the safety of an outdoor location or prove that every generated sentence is appropriate.

Structured outputs with Zod and Ollama

Small models can produce plausible-looking values that are still wrong for an application. An activity ID such as seated-movement-001 might look perfectly reasonable while pointing to absolutely nothing in the catalogue.

I constrain the response using a JSON schema generated with Zod 4. The eligible activity IDs form an enum in the response schema, which is passed to Ollama through its format parameter.

The backend then validates the returned JSON and checks the chosen activity against the eligible list. The purpose is to constrain the response structure and permitted IDs, while retaining application-level checks.

That improves reliability. It does not make the model's instructions universally correct or turn generative text into a fully deterministic system.

Next.js and TypeScript

The app uses Next.js 16 with the App Router and TypeScript, keeping the interface and API route in one project.

The request flow is:

  1. The browser collects the four preferences.
  2. The backend validates them and filters the activity collection.
  3. Ollama runs the local model using those constraints.
  4. The backend validates the response and adds the schedule.
  5. The interface displays the activity card.

Plain CSS provides labelled controls, visible keyboard focus and a responsive layout. The app does not depend on an external stylesheet or font CDN.

I used deep teal, warm cream and a small terracotta accent to give the interface a calm, welcoming feel. The main button is easy to find, and the result is designed to be read quickly.

Why open weights mattered

Open weights let me run inference on my own hardware and keep the local generation flow independent of a hosted AI API.

They also let me choose a model that fits the task, change the prompt and edit the activity collection. Another developer can inspect the code and run the application in their own environment.

The tradeoff is that I own the setup too: model downloads, available memory, slow starts and connection errors. The demo's error message is a practical reminder of that responsibility.

Gemma is used under its own model terms. The application's licence does not replace those terms.

Hackathon Experience

I built Step Out during the first week of Hacktoberfest 2026.

What I learned

A small model can be useful when its task is focused. Gemma 3 1B did not need to plan my entire life. It needed to turn a few preferences and eligible activities into a short outdoor break.

Application code still has a job. Input validation, activity filtering, time calculations and response checks belong in the backend. A persuasive prompt is not a substitute for those controls.

An app can succeed by helping someone leave it. I wanted one clear plan that the user could take outside. More time in the interface would work against that goal.

The courtyard test was the part that made the project feel real. After working on prompts and schemas, I finally followed the instructions myself.

For once, closing the laptop was part of the acceptance test.

Top comments (0)