This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
Most apps try to keep your attention.
Touch Grass is built around the opposite idea: choose something to do outside, let the app brief you, put your phone away, and only come back when you're ready to record what happened.
The phone is the scoreboard. The game happens outside.
Touch Grass is an open-source outdoor challenge game designed to help young people spend more time in the physical world through small, achievable missions. Instead of endlessly scrolling for entertainment, players receive an objective that gives them a reason to step outside, observe their surroundings, explore, or interact with other people.
How the game works
- Choose a vibe. Pick Observe, Calm, Discover, Explore, Active, Social, or Surprise Me.
- Generate a mission. Choose a duration of 10, 20, 30, or 60 minutes and receive a concrete outdoor challenge.
- Read the briefing. Understand the objective and what you'll need to record afterwards.
- Put your phone away. Start the mission and go outside.
- Record the experience. Return when you're done and write a short mission log describing what happened.
- Earn progress. The game evaluates the submitted evidence and calculates the XP awarded under its progression rules.
The goal isn't to get players to spend more time inside the app. It's to give them a reason to leave it.
Screenshots
The main experience
Creating an outdoor mission
Spaces and competition
Global leaderboard
Demo
Watch the video: Touch Grass demo on YouTube
Try the application: Touch Grass β Live Demo
Explore the source code: GitHub repository
Code
The project is open source: RebeccaAyodele/Touch-grass on GitHub.
I built Touch Grass as a pnpm/Turborepo monorepo, separating the web application, API, shared contracts, game rules, AI workflows, and database access.
Touch-grass/
βββ apps/
β βββ web/ # Next.js application and API client
β βββ api/ # Express API, routes, controllers, middleware
βββ packages/
βββ shared/ # Shared TypeScript types and Zod schemas
βββ game/ # XP, levels, scoring, deterministic rules
βββ ai/ # Provider adapters, prompts, workflows, fallbacks
βββ db/ # MongoDB client, indexes, repositories
Running it locally
pnpm install
pnpm --filter api dev
pnpm --filter web dev
Configure the environment variables using the provided .env.example files and the repository README. A reachable MongoDB instance is required for registration and persistent game data. For initial development, the mock AI provider can be used.
The API and web application run separately, so the API must be configured correctly for the web client to communicate with it.
How I Built It
1. Designing an experience that encourages people to leave the app
One of the central product decisions was that a mission should not require players to keep checking their phones.
The important moment in the interface is when a mission starts and the player can put the phone away. The app provides the objective upfront, lets the player go and experience it, and provides a way to record what happened afterwards.
The application includes a mobile-oriented interface, large controls, clear mission states, and a PWA manifest so it can be installed on a device's home screen.
The current implementation supports a resumable active mission. However, installing the PWA does not mean the entire game works offline; offline support is still incomplete.
2. Using AI for mission variety and evidence evaluation
AI is useful for making outdoor missions varied and engaging. It can turn a simple intention, such as wanting 20 minutes of calm, into a more specific activity with an objective and a short briefing.
It can also evaluate a player's mission log against the challenge they received.
But I did not want an AI response to become the authority for the entire game.
The AI is not responsible for deciding a player's identity, awarding arbitrary XP, authorizing duplicate claims, or independently determining every state transition. Those responsibilities belong to the application's validation and game logic.
Provider responses are validated before they enter the game flow. Challenge generation has a curated fallback path when generation fails. Evidence evaluation fails explicitly when the configured provider is unavailable rather than treating an outage as a successful evaluation.
The AI integration sits behind a provider interface, keeping application logic separate from model-specific implementation. The architecture can accommodate a mock provider during development and compatible hosted or local inference providers without requiring the rest of the application to be rewritten.
This also means the model can be changed without changing the game's core progression rules.
3. Keeping progression deterministic
One of the most important architectural decisions was separating AI-generated content from game rewards.
XP calculations, levels, duration checks, and reward validation live in packages/game, not in the browser and not inside the model's response.
Each mission has a base XP value and a maximum evidence bonus based on its duration. The server compares the active mission duration with the player's selected duration. If a mission ends early, the duration-based reward is reduced proportionally; completing the selected duration preserves the full calculated duration reward.
The server then applies the reward, updates the challenge, and records the completion.
The principle is simple: the server calculates the reward using game rules; the model does not get to invent it.
This is important because AI output should be treated as untrusted input, even when it comes from a model that usually returns well-formed responses.
4. Preventing duplicate rewards and inconsistent state
A game that awards XP needs to account for retries, repeated requests, and concurrent submissions.
Touch Grass uses MongoDB to persist accounts, progression, generated challenges, submitted evidence, completion records, Spaces, memberships, and leaderboard-related data.
Database access is kept behind repositories in packages/db. The API uses indexes for lookups, uniqueness, and ranking. Mission completion is designed to update the reward, challenge state, and completion record together in a transaction.
A unique constraint on (userId, challengeId) helps prevent the same user from claiming a mission's reward more than once, including when a request is retried.
These safeguards matter because a successful-looking response is not enough. The underlying state must remain consistent.
5. Sharing contracts across the application
The web app, API, AI workflows, game logic, and database layer share TypeScript types and Zod schemas from packages/shared.
These contracts define the application's common vocabulary for challenges, evidence, users, completions, Spaces, durations, experience, and statuses.
Without shared contracts, each layer could interpret the same object differently. A challenge accepted by the UI might not satisfy the API, or a model response might introduce a shape the game logic was not designed to handle.
Validating data at boundaries makes those mismatches easier to catch.
6. Building social competition with Spaces
Touch Grass includes Spaces that users can create or join using an invite code. Members can view other members and compare their progress on a Space leaderboard.
There is also a global leaderboard and API-level support for player profiles and mission history.
The idea is to support asynchronous competition. Players do not need to be online simultaneously or turn outdoor activity into a live feed for their friends.
The social layer is meant to provide motivation without making constant app engagement the objective.
7. What proved harder than expected
Generating a mission was not the hardest part. Deciding what the AI should not be allowed to control was.
I had to think through cases such as malformed model output, a generated mission that does not match the selected duration, a failed provider request, repeated completion submissions, database failures, and users attempting to access another person's data.
These cases influenced the architecture: explicit validation, provider boundaries, server-side reward calculation, persistence rules, and failure paths all matter as much as the happy path.
There are still incomplete parts:
- Photo evidence storage and the complete browser upload flow are not finished end-to-end.
- Spaces do not yet have a complete group-challenge lifecycle.
- Leaderboards currently use total XP rather than weekly or seasonal rankings.
- The profile interface does not expose every API capability, including mission history.
- The application still needs production verification against its configured MongoDB instance and AI provider.
- Offline support and some reward-value reconciliation work remain.
I would rather state these limitations clearly than describe planned integrations as if they had already been verified.
Why Does Open Innovation Matter?
Open innovation matters because a project like Touch Grass should not have to depend on one model vendor or a closed ecosystem to evolve.
Keeping the AI integration behind a provider interface gives developers room to experiment with different models and inference setups while retaining the same application contracts and deterministic game rules. A developer can use a mock provider to work on the application, then configure a compatible hosted or local model without rebuilding the entire system around that provider.
The separation also makes experimentation safer. Changing the model used to generate a mission should not change how XP is calculated, how duplicate claims are prevented, or how completed missions are persisted.
Open source also makes the architecture available for inspection and improvement. Other developers can contribute alternative mission-generation workflows, improve validation, add new mission types, or build social features without having to treat the existing implementation as a black box.
For me, the lesson was that openness is not just about making source code public. It is also about creating boundaries that allow people to replace, inspect, and improve individual parts of a system.
Prize Categories
Best Use of MongoDB Atlas
MongoDB is the persistence layer for player accounts and progression, generated challenges, evidence submissions, completion records, Spaces, memberships, and leaderboard-related data.
The project uses repositories to isolate database access, indexes to support queries and uniqueness, and transactional completion updates to help keep reward and mission state consistent. The unique user-and-challenge constraint helps protect against duplicate reward claims.
Best Use of Gemma
Touch Grass uses Google Gemma 4 26B A4B IT (google/gemma-4-26b-a4b-it:free), accessed through OpenRouter, for its AI-powered mission generation and evidence-evaluation workflows.
Gemma helps transform a player's selected vibe and time commitment into a concrete outdoor mission, then evaluates the player's mission log against the challenge they received.
I deliberately kept the model separate from the game's authoritative logic. Gemma can generate mission content and assess submitted evidence, but it does not directly award XP, control player progression, or authorize duplicate completion claims. Those decisions are handled by deterministic server-side game rules and validation.
This separation allows Gemma to provide flexible, varied interactions while keeping rewards and persisted game state under application control. It also makes the AI integration easier to maintain and replace through the project's provider abstraction.
Final Thoughts
An app designed to get people outside can easily become another app that demands their attention.
Every additional notification, screen, leaderboard, or interaction creates a question: does this feature make the outdoor experience better, or does it make the phone more important?
Touch Grass is my attempt to make the screen temporary. AI provides a starting point, game mechanics provide a reason to participate, and the actual experience happens away from the device.
Close the app. Go find out what happens.





Top comments (0)