DEV Community

Piyush Baraskar
Piyush Baraskar

Posted on

Outdoor Nudge: One Small Mission to Get You Outside

Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass

There is a particular kind of afternoon I wanted to build this for.

You've been sitting at your desk for hours. Your brain feels a little fried, you're restless, and you know you probably need a break. You open another tab, check your phone, scroll for a while, and somehow another 20 minutes disappear.

You know you should step outside. You just can't think of what to do once you get there.

A walk sounds boring. The weather might be terrible. You don't have the energy for anything ambitious. And generic advice like "touch grass" isn't exactly helpful when you're already trying to figure out how.

That small moment is what led me to build Outdoor Nudge.

The idea is simple: tell it how you feel, and it gives you one small outdoor mission to do for roughly 20 minutes. Something concrete enough that you can stop thinking about taking a break and actually take one.

No endless recommendations. No motivational lecture. Just one nudge.

What I Built

Outdoor Nudge is a local-first AI app that turns your mood and the current weather into one practical outdoor activity.

You might type:

restless and stuck at my desk

Outdoor Nudge considers your mood, local weather, and the time of day, then generates a mission that fits the situation.

Instead of getting "go for a walk," you might get something like Shadow Sprint Intervals: head outside, move briskly along the sunny side of your building, use nearby landmarks for a few short intervals, and finish with some shoulder rolls.

The suggestion includes:

  • A mission title that gives the activity a little personality.
  • Step-by-step instructions so you know exactly what to do.
  • Why it fits your mood and the current conditions.
  • What to bring, if anything.
  • Duration, energy level, and activity category to help you decide whether it feels manageable.

The goal isn't to turn a break into another productivity task. It's to make getting outside easier to start.

Why not just ask a chatbot?

Because generating an idea is the easy part. Making it useful in the real world is harder.

A language model might suggest sitting on a park bench when there is no bench nearby, recommend an energetic activity when you're exhausted, or suggest something that makes no sense in the current weather.

Outdoor Nudge is designed around that gap between a plausible-sounding answer and an activity someone can actually do.

It checks the model's output against rules for safety, realism, energy level, activity category, and repetitive suggestions. If a suggestion fails those checks, the backend asks the model to try again rather than displaying the rejected result.

The model comes up with the idea. The application has the final say on whether that idea passes its checks.

That's an important distinction for an app whose suggestions are meant to be followed outside, not just read on a screen.

Demo

Try it yourself: Start Outdoor Nudge locally using the instructions in the repository.

Repository: github.com/PIYUSH-NEXTGEN/Outdoor-Nudge

The project currently runs locally, so I haven't included a public demo URL here. A short screen recording would be a good way to demonstrate the full experience, including mood input, weather, the generated mission, and the option to request an easier or different activity.

A useful demo scenario:

  1. Enter restless and stuck at my desk.
  2. Let the app fetch the weather, or enter weather conditions manually.
  3. Generate a mission and inspect the steps, energy level, and explanation.
  4. Click Give me another to get a different suggestion.
  5. Click Make it easier to request a lower-energy activity.
  6. Open the history panel to see previous nudges.

I'd especially show the last two interactions because they demonstrate that this is more than a single prompt sent to an LLM.

Code

Outdoor Nudge

Tell it how you feel, get one small outdoor mission back. Built for those afternoons when you know you should step outside but can't think of what to actually do.

Report a Bug

Table of Contents

Overview

Outdoor Nudge is a tiny local first web app. You type your mood in plain words ("restless and stuck at my desk"), it looks up the live weather for your spot using Open-Meteo, and a local LLM (Ollama, gemma3:4b by default) comes up with exactly one concrete outdoor task of about 20 minutes.

Each nudge has a title, step by step instructions, why it fits your mood and weather, what to bring, an energy level, and a category. The front end is a single page (…

The repository includes the application, prompt and validation logic, weather integration, local history, tests, and setup instructions.

It's open source under the MIT license, so you're welcome to explore it, run it locally, report issues, or contribute improvements.

How I Built It

I wanted the application to be small enough to run without a complicated setup, but I didn't want to sacrifice the logic that makes its suggestions useful.

That led to a deliberately straightforward stack:

Layer Technology Purpose
Frontend HTML, CSS, JavaScript Responsive interface, mood input, mission cards, history, and model comparison
Backend Python, FastAPI, Uvicorn API endpoints, request handling, orchestration, and retry logic
AI Ollama + Gemma 3 4B Local generation of structured outdoor missions
Weather Open-Meteo Live weather conditions without an API key
Validation Pydantic + custom Python rules Validate the output structure, activity constraints, and safety checks
Storage Local JSON file Save previous nudges and completion status
Testing Playwright + Python tests Test the interface, quality checks, and validation rules

No React. No frontend build step. No hosted database. No paid AI API.

I kept the stack intentionally boring because I wanted the interesting engineering to live in how the app makes decisions, not in how many dependencies it uses.

The architecture

Here's how a request moves through the application:

                 USER
                   |
                   v
       +------------------------+
       |   Browser Interface    |
       |   static/index.html    |
       +------------------------+
                   |
          +--------+---------+
          |                  |
          v                  v
    GET /api/weather    POST /api/nudge
          |                  |
          v                  v
    +-------------+   +------------------+
    | Open-Meteo  |   | FastAPI Backend  |
    | Live Weather|   |                  |
    +-------------+   +------------------+
                             |
                             v
                  +----------------------+
                  | Mood + Weather +     |
                  | Time + Energy +       |
                  | Category + Exclusions|
                  +----------------------+
                             |
                             v
                  +----------------------+
                  | Prompt Construction  |
                  +----------------------+
                             |
                             v
                  +----------------------+
                  | Ollama + Gemma 3 4B  |
                  | Generate JSON        |
                  +----------------------+
                             |
                             v
                  +----------------------+
                  | Schema, Realism &    |
                  | Safety Validation   |
                  +----------------------+
                             |
                      +------+------+
                      |             |
                   Rejected      Accepted
                      |             |
                      v             v
                 Retry with     Save to local
                 feedback       history.json
                      |             |
                      +----->       v
                              Return JSON
                                   |
                                   v
                            Render Mission
Enter fullscreen mode Exit fullscreen mode

The weather lookup is a separate part of the flow. The browser can request current conditions through the backend, which uses Open-Meteo. The nudge endpoint then uses the available weather information alongside the mood and other request settings.

Let's look at the parts that make the application work.

1. Turn an ordinary sentence into useful constraints

The user doesn't have to select a formal mood category or fill out a long questionnaire. They can write something natural, such as:

  • "I'm exhausted and don't want to do much."
  • "Feeling restless after sitting all day."
  • "I need a change of scenery."
  • "I'm feeling lonely and could use some human interaction."

The backend uses energy_pool() to estimate whether the person needs a low-, medium-, or high-energy activity. It then uses pick_category() to select an activity category, taking category limits and least-recently-used weighting into account.

The request can also include the user's location, selected model, a manual weather override, previously excluded titles, and an easier-mode flag.

This is useful because the model shouldn't have to infer every constraint from a sentence on its own. The application can make some decisions explicitly before generation begins.

2. Give the model the right context

The prompt builder in app/prompts.py combines the relevant information into a structured instruction for Gemma.

Conceptually, the input looks like this:

Mood: Restless and stuck at my desk
Weather: Clear sky, 22°C
Time of day: Evening
Energy: High
Category: Movement
Previously used titles: [excluded titles]

Task:
Generate one specific outdoor mission.
Return the required JSON structure.
Follow the activity and safety constraints.
Avoid generic suggestions and repeated ideas.
Enter fullscreen mode Exit fullscreen mode

This is an illustrative representation of the context, not a literal dump of the application's complete system prompt.

The model receives more than just the mood. It has conditions to work within, which helps make the resulting mission more relevant to the situation.

3. Use Gemma locally

Outdoor Nudge uses Gemma 3 4B through Ollama by default.

Ollama provides the local model runtime. The backend sends the prompt to the configured Ollama server, receives the model's response, and extracts the JSON needed by the application.

The important design choice is that the model is not accessed through a hosted AI API. Mood text is processed locally, and the app doesn't require an OpenAI or Gemini API key.

Weather is the exception to the local-only approach: live weather requires an internet connection to Open-Meteo. If that service is unavailable, the user can enter the conditions manually.

Local inference also comes with a practical trade-off. Generation speed depends on the machine and the model being used. On a CPU-only machine, a request can take a minute or two.

4. Don't trust the first answer just because it came from an LLM

This is probably the part of the project I find most interesting.

A model can return perfectly valid JSON and still give a terrible recommendation.

For example, a response might be structurally correct but tell someone to climb somewhere, approach a road, touch unknown plants, or rely on an object that may not exist. A generic "go for a walk" answer can also satisfy a loose prompt while failing the actual purpose of the application.

So I separated generation from validation.

The validate_nudge() function checks the response structure, category and energy consistency, instruction count, and banned verbs or venues. The safety_violations() function applies additional checks to the proposed activity.

The checks cover cases such as:

  • Climbing, roads, and water-related risks.
  • Eating or tasting things found outside.
  • Touching plants or litter with bare hands.
  • Assuming a bench, bin, notebook, or other object is available.
  • Generic suggestions that don't describe a concrete activity.
  • Lonely-mood suggestions that fail to include meaningful human contact.
  • Inappropriate activities after dark.

If a response fails, the application retries generation, providing feedback about what went wrong. It tries up to four times before returning an error rather than showing an invalid suggestion.

Generate mission
      |
      v
Parse JSON
      |
      v
Validate structure and constraints
      |
      +---- Pass? ---- Yes ----> Save and display
      |
      No
      |
      v
Explain failure to model
      |
      v
Retry (up to 4 attempts)
      |
      +---- Still invalid? ----> Return an error
Enter fullscreen mode Exit fullscreen mode

These checks are deterministic rules, not a guarantee that every suggestion is safe. The project is a small local application, and the user should still exercise common sense when following an outdoor activity.

Still, separating these rules from the model is valuable: I can add a new restriction, write a test for it, and change the application's behavior without relying entirely on a better prompt.

5. Make the next suggestion different

Another small problem: if you ask an AI for another idea, it can give you essentially the same answer with a different title.

Outdoor Nudge tracks recent titles and excludes previously used ideas. It also limits repeated categories and uses least-recently-used weighting to encourage variety.

The Give me another button carries those exclusions forward. Make it easier requests a lower-energy option.

There's also a model comparison mode, so you can select two locally available Ollama models and compare their suggestions side by side.

That gives the application a little more depth than generating one answer and stopping there.

6. Keep history simple

Accepted nudges are stored in data/history.json, which is gitignored. The interface has a collapsible history panel and a way to mark a mission as completed.

I chose a JSON file instead of introducing a database. This is a single-user, local-first application, so a database would add setup and maintenance without solving a problem I currently have.

The history is written atomically, with a duplicate-title guard. It stays local rather than requiring an account or remote service.

7. Test the rules, not just the happy path

For an application that recommends real-world activities, testing only whether the page loads isn't enough.

The repository includes several layers of tests:

  • Offline safety tests: Around 70 cases covering prohibited activities, assumed objects, nighttime restrictions, generic walks, and other edge cases.
  • Quality retests: A 15-case harness covering different moods, weather conditions, time contexts, response structure, plausibility, clichés, and duplicates.
  • Browser tests: Playwright tests covering 14 page flows, with screenshots for desktop, mobile, model comparison, history, and weather fallback.

The live quality and safety retests require the backend and Ollama to be running. The offline validator tests can run without a model.

I wanted the rules to be testable independently of the AI, because changing a model or prompt shouldn't silently remove a safety constraint.

Why Does Open Innovation Matter?

I could have built this around a hosted AI API. It might have been a simpler way to get started.

But I wanted Outdoor Nudge to be something you could run on your own machine without an account, a paid API key, or sending your mood to a third-party AI service.

Mood is personal information, even when someone is only typing that they're exhausted, lonely, or having a bad day. I didn't want getting a small outdoor suggestion to require sharing that information with a remote model provider.

Using Gemma 3 4B through Ollama made local inference practical. The model runs on the user's machine, and the application can work without a hosted AI service. Open-Meteo is still used for live weather, so the app needs internet access for that feature unless the user enters conditions manually.

Open models also give us flexibility. We can try another model already supported by Ollama, compare outputs, adjust the prompt, and build our own validation around the model instead of depending on a single proprietary endpoint.

That freedom is particularly useful for this project because the model isn't the whole product. The weather integration, category selection, safety rules, retry loop, and history all shape the final experience.

Open innovation gave me the freedom to build the AI around the experience I wanted, instead of building the experience around an AI provider's API.

And the end goal is deliberately small: help someone close their laptop for a little while and do something in the world around them.

Prize Categories

Gemma: Outdoor Nudge uses Gemma 3 4B as its default local model through Ollama. The model generates structured outdoor missions, while application-side validation checks each suggestion before it reaches the user.


Outdoor Nudge isn't trying to replace a therapist, a fitness app, or a human friend.

Sometimes you don't need another app telling you how to improve your life. You just need one good reason to step away from your screen.

So that's what I built: one mood, one look at the weather, one small mission.

Now close the tab and go try it.

Top comments (0)