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

I've always been fascinated by the invisible layers of a place. You walk past a building every day and never wonder who lived there, what happened in that street, or why that monument exists. You take a photo, and all of that context stays invisible.

Frames of Truth is my attempt to fix that.

It's an Android app that takes a photo, reads its GPS coordinates from the EXIF metadata, and uses open-source AI models to tell you the story of the place where the photo was taken. Not the story of what's in the photo — the story of where you were standing when you took it.

What I Built

Every smartphone photo carries hidden metadata: when it was taken, with what device, and — if geolocation was enabled — where. That last piece of information is a GPS coordinate. It tells you where the camera was, not necessarily what the camera was pointing at.

That distinction matters. If you point your phone at a distant hill and shoot from your backyard, the GPS gives your backyard. The photo has your perspective, not the hill's location.

Most apps ignore this. They geotag the photo with a generic address and move on. Frames of Truth doesn't. It treats the GPS coordinate as the start of an investigation.

Here's what happens when you load a photo:

  1. The app reads the GPS coordinates from the EXIF metadata
  2. It reverse-geocodes the location (Photon, Nominatim)
  3. It finds nearby points of interest (Geoapify, Overpass)
  4. It pulls historical context from Wikipedia and Wikidata
  5. It sends the photo to a vision model to describe what's in it
  6. An LLM writes a story in three sections: Location, Image, Curiosity

If you want more, there's a second button that generates a deeper investigative report with six sections: Location, Surroundings, Wikipedia & Wikidata, Recent news (via GDELT), Photo metadata, and an Investigative hypothesis.

The UI is in 7 languages: Italian, English, French, German, Spanish, Portuguese, Dutch.

Who is it for?

For anyone who walks past places every day and never wonders what's behind them. For travelers who want context without opening ten tabs. For people who love going outside and want a reason to explore their own neighborhood.

How does it get people off the screen?

The app is the opposite of doomscrolling. You have to go outside to use it. You have to walk somewhere, point your camera at something, and come back with a real photo of a real place. The screen is the shortest part of the experience. The place is the point.

Demo

APK Download: Latest release on GitHub

Install it on any Android device (7.0+). On first launch, open the ⚙️ menu and enter your own API keys (Groq, Geoapify — both have free tiers).

Screenshots

  • The main screen with the "Take or choose a photo" prompt

Main screen

  • A generated story with the three sections

Image analysis

  • The investigative report with collapsible sections

Deep investigation

  • The API key settings modal with the .txt import feature

API keys modal

Code

The full source code is available on GitHub under the MIT license:

Repository: github.com/vincenzodesanctis/frames-of-truth

Tech Stack

Layer Technology
Mobile Android (Kotlin) with Chaquopy for Python integration
Backend Python (Flask) as a local HTTP server
Frontend WebView with HTML/CSS/JS
AI Models Open-weight via Groq (Qwen 3.8, GPT-OSS) and OpenRouter (Llama 3.3)
Geocoding OpenStreetMap (Photon, Nominatim, Overpass, Wikipedia, Wikidata)
EXIF/HEIC MetadataExtractor (Drew Noakes)

How I Built It

The open-source AI stack

The entire AI pipeline is built on open-weight models:

  • Vision analysis: Qwen 3.8 via Groq
  • Story generation: GPT-OSS via Groq
  • Fallback narration: Llama 3.3 via OpenRouter
  • Vision fallback: Gemini via REST

The choice of open-weight models wasn't an afterthought — it was the design constraint. I wanted to be able to swap providers without rewriting the app, and to avoid being locked into any single vendor.

Running Python on Android

I used Chaquopy to run Python 3.8 directly on the phone. Flask serves a local HTTP server on 127.0.0.1:5000, and a WebView loads the UI from there. It's a bit unusual, but it means I can reuse all my Python logic — geocoding, POI discovery, Wikipedia lookups, LLM calls — inside an Android app without rewriting anything in Kotlin.

Technical challenges I had to solve

HEIC support: Modern Android phones and iPhones save photos as .heic files by default. Python's PIL library doesn't read HEIC natively. My solution: detect HEIC files on the Kotlin side (reading the magic bytes ftyp), extract the EXIF metadata before converting, then convert the image to JPEG for the vision model. This way I preserve GPS coordinates even though the file format changes.

GPS redaction on Android 10+: Starting with Android 10, the OS redacts EXIF GPS data from any file read through a content URI — unless the app requests the ACCESS_MEDIA_LOCATION permission. Without it, every photo comes back with lat: null. With it, the coordinates come through intact. This one took me days to figure out, and required using MediaStore.setRequireOriginal() on the URI.

User-configurable API keys: Instead of hardcoding my keys, I built an in-app settings menu with a modal that lets users enter their own keys or import them from a .txt file. The keys are saved in SharedPreferences and pushed to the Python layer via a JavaScript Interface bridge. They never leave the device.

📴 A note on connectivity

The challenge mentions "an open-weight model that works with no signal." Frames of Truth currently requires an internet connection for AI inference — but the architecture is designed to be portable to on-device models.

Here's how:

  • The AI pipeline is provider-agnostic. Swapping from Groq to a local model means changing one function call.
  • The vision and narration layers are separate, so you can run a small local vision model on the phone and keep the narration online (or vice versa).
  • The Python backend runs entirely on the device via Chaquopy. Nothing needs a server.
  • The geocoding layer (Photon, Nominatim) can be swapped for a pre-downloaded OpenStreetMap extract for full offline geocoding.

The reason I built it this way is that open-weight models are moving fast. Today they need a GPU in the cloud. Tomorrow they'll run on your phone. Frames of Truth is ready for either world.

Why Does Open Innovation Matter?

Building this app on open-weight models wasn't just a cost-saving decision. It changed how I think about software.

What open innovation made possible

  1. No vendor lock-in. If Groq changes its pricing tomorrow or shuts down its free tier, I can swap to OpenRouter, Together AI, or a self-hosted model. The app code doesn't change. Only one API call changes.

  2. Portability. Because the AI pipeline is provider-agnostic, the app can eventually run a small local model on the phone itself. Closed APIs make this impossible — you can't run GPT-4 on a phone, but you can run Llama 3.2 or Gemma 2.

  3. Transparency. Anyone can read the pipeline end-to-end. They can see exactly which model processes the photo, what prompt is sent, and how the response is structured. There's no black box.

  4. Community-driven improvement. The whole project relies on open data and open infrastructure: OpenStreetMap for geocoding, Wikipedia and Wikidata for context, GDELT for news. Every one of these is maintained by a global community of volunteers.

  5. Cost control for indie developers. A solo developer can't afford to pay per-token for a closed API on a hobby project. Free tiers on Groq and OpenRouter make this kind of experiment possible.

What a closed API wouldn't have allowed

If I had built this on a closed vision API, I would have been locked into:

  • Their pricing (which changes without notice)
  • Their rate limits (which often break at the worst moment)
  • Their terms of service (which often forbid certain use cases)
  • Their model choices (which they can deprecate whenever they want)

With open-weight models, I keep control. That's the whole point of open innovation.

My Agent Session

I did not use DevRelay for this project, so there's no agent session to embed. The whole project was built iteratively by hand, testing each layer on a real Android device and debugging via Logcat.

What I Learned

Building this app taught me that open-source AI isn't just a cost-saving alternative to closed models. It's a different way of building software.

When I can see the model, swap the provider, and read the pipeline end-to-end, I feel like I'm actually in control of the product. I don't wake up one day to find that a service I depend on has changed its terms, or started charging 10x more, or shut down its free tier.

For an indie project like this, that kind of control is everything.

Final Thought

The best part of building Frames of Truth was that it made me want to go outside and test it. Every time I took a photo, I learned something new about a place I thought I already knew.

That's what "Touch Grass" should mean: not abandoning technology, but using it to reconnect with the world. Open-source AI, done right, is a bridge — not a wall.

If you try the app, let me know what your neighborhood looks like through its eyes. 🌍

Built with ❤️ for the Hacktoberfest Open-Source AI Challenge 2026.