Skip to content
Contact
Case Study · Consumer · Fitness

Klaimo: Cycling Tracking Where You Conquer Real Territory

Gamified cycling-tracking app for iOS & Android – as you ride, you conquer real areas on the map, with a live map, leaderboards and teams. An in-house product by SB-Techworks.

FlutterGo / Cloud RunMapboxGarmin APIRevenueCat
Fahrrad-Tracking-App Klaimo von SB-Techworks: Live-Karte mit eroberten Territorien
500,000+App downloads
24 hResponse time (Mon–Fri)
100 %You own the code
1Point of contact, start to launch

The Challenge

A fitness app has to record a ride and evaluate it. A gamified cycling app, in which riding claims real-world territory, has to turn that same recording into something fundamentally different: a line has to become an area — and that area has to be reconciled with everything on the map that already belongs to someone. The centre of gravity shifts out of the frontend and into geometry: building polygons, merging them, intersecting them, subtracting them. That is precisely the part that decides how the game feels and what it costs to run.

What makes it harder is that raw GPS is not a clean path. Positions drift, jump around in dense urban canyons, arrive at uneven intervals and cut out in tunnels or whenever the signal drops. Turn such a sequence of points straight into a polygon and you get self-intersections, outliers and geometrically invalid shapes — and invalid geometry brings down every operation layered on top of it. So before any of the game mechanics comes data preparation.

Then there is the question of authority. The moment leaderboards and teams exist, the result has to be the same for everyone. A world state that each handset computes for itself would be neither consistent nor verifiable. And the cost profile behaves differently from plain tracking: recording a route costs roughly the same every time, intersecting new areas against a map that keeps filling up does not. Processing geospatial data in real time while still rendering a smooth live map was the actual task — on top of subscriptions across two stores, a wearable integration and the store launch, all of it handled end to end in-house.

The Approach

The first decision was where to draw the line between app and server. The Flutter app records and displays; the backend decides what counts. The compute-heavy geo math — areas, polygons, intersections — runs in a Go service on Google Cloud Run that scales automatically under load. The map therefore rests on a single source of truth, leaderboards and team scores stay comparable, and the computation does not sit on a device that is already busy logging positions for the length of an entire ride.

Go suits this kind of work because polygon operations are CPU-bound rather than waiting on I/O: compiled code, fast startup, a small memory footprint, concurrency without much ceremony. Cloud Run adds load-based autoscaling on top — fewer instances during quiet periods, more when demand arrives. The load profile of a leisure app is predictably uneven (after work, weekends, good weather), and request-based autoscaling is built for exactly that. The flip side: the service has to be stateless, and any state belongs in the data layer.

On the map, claimed territories are drawn through custom Mapbox layers rather than as individual overlay objects placed on top of the map. The difference is in the rendering: areas expressed as layers are drawn and styled by the map renderer itself and remain part of the map while panning and zooming, instead of floating above it as an ever-growing set of separate elements. The live map, the leaderboards and the team views all read from the same server-side state.

Anything that is not the core was integrated rather than built: subscriptions run cross-platform through RevenueCat, so there is one subscription status instead of two separate state machines for the App Store and the Play Store; fitness data comes in via the Garmin API, so riders can keep using the recording hardware they already own. The guiding principle: build what makes the product what it is — geo logic, game mechanics, map rendering — and use established services for the rest. Architecture, backend and the launch in both stores were all in one pair of hands.

What Was Built

  • Real-time territory conquest
  • Interactive live map (Mapbox)
  • Leaderboards & teams
  • Subscription model (RevenueCat)
  • Garmin integration
  • Go geo-backend on Cloud Run

Technical Decisions

Area computation in the backend rather than on the device

Computing locally would be faster to respond and cheaper to operate — but the game state would only ever be as trustworthy as the handset, and every phone would carry its own version of the world. Intersecting a ride with other players' territory needs data that only exists in full on the server anyway. The price is a dependency on connectivity: the final outcome of a ride is only settled once the backend answers, and the app has to bridge that intermediate state in a way that still makes sense to the rider.

Go on Cloud Run instead of an always-on server

The alternatives were a permanently running VM — predictable, but too small under peaks and paid idle time the rest of the time — or Kubernetes, with full control and the operational overhead that comes with it, permanently resting on one person. Cloud Run takes a container and handles scaling itself; Go contributes fast startup and a small memory footprint, which counts immediately in exactly that environment. The price: statelessness is mandatory, longer computations have to be broken into units that fit inside a response, and cold starts have to be factored into how the service is cut. On top of that, the geospatial ecosystem is thinner than in, say, Python — so more of it gets written by hand.

Custom Mapbox layers instead of standard overlays

The obvious route would have been to place each claimed area on the map as an individual polygon overlay. That holds up with a handful of shapes and turns unpleasant as soon as many areas are visible at once and keep changing while zooming. Custom layers move drawing and styling to where vector data belongs: into the map renderer. The price is a tighter coupling to a commercial mapping service, with usage-based costs and its SDK release cycle attached — styles and data sources are then modelled in its terms, not the UI framework's.

One Flutter codebase instead of two native apps

Two native apps would win on platform fidelity and on access to new OS features, but they mean building twice and maintaining twice, indefinitely — the wrong price in a one-person operation. Flutter gives one codebase and one release cycle for iOS and Android. The cost shows up exactly at the platform-near edges: map rendering and location capture depend on native SDKs, so you rely on the quality of those bindings, and new platform features only become available once they catch up.

Tech Stack

FlutterGo / Cloud RunMapboxGarmin APIRevenueCat

Outcome

An in-house product by SB-Techworks – from architecture and the Go backend to launch, published on the App Store and Play Store. Klaimo shows that even compute-heavy consumer apps with live geodata can ship to production as a solo project.

What This Means for Similar Projects

For comparable projects — anywhere movement or sensor data turns into a binding result — the decisive question is not how to record GPS, but where it is decided what counts. As soon as leaderboards, competitions or rewards are involved, that computation belongs on the server; retrofitting it later is close to impossible, because past states cannot be reconstructed without a server-side source. It is equally worth looking early at how the workload grows: recording costs a constant amount, intersecting geometry gets more expensive with every additional record in the map. A stateless, autoscaling compute service alongside the application itself is a durable pattern for that — and not only for geospatial data.

The second transferable point is about scope. Every supporting function you build yourself — billing, subscription management, map rendering, integrations with third-party platforms — is maintenance forever, and maintenance grows while development capacity does not. In practice, what decides feasibility for a gamified fitness app is less the game idea than how much gets custom-built around it. The better move is to concentrate original development on the part that makes the product distinguishable and accept established services for everything else — including the dependency that comes with them.

Frequently Asked Questions

Why doesn't the area computation run directly in the app?

Because the result has to be binding. Leaderboards and team scores assume that everyone sees the same map state and that the computation does not depend on an individual handset. On top of that, intersecting a new area with already claimed territory needs data that only exists in full on the server — and the computational load should not sit on the phone that is simultaneously recording the ride.

How does a ride turn into claimed territory?

A recording starts out as nothing more than a sequence of positions, i.e. a line. An area is derived from it and then reconciled with the existing map state: polygons are merged, intersected and subtracted from one another. The effort lies less in the recording than in these set operations — and in the clean-up of the raw data that has to happen first, because invalid or self-intersecting geometry makes every subsequent computation worthless.

Why a Go backend rather than a backend in the app's own language?

One language fewer would be convenient, but the work here is CPU-bound geometry, not waiting on a database or the network. Go is compiled, starts quickly, needs little memory and brings concurrency without much ceremony; in an environment that scales per request, that shows up directly. The downside is a thinner geospatial ecosystem than in, say, Python, plus a second language that has to be maintained.

How do subscriptions work when an app ships in both stores?

The App Store and the Play Store each have their own purchase flow, their own receipt validation and their own events for renewal, cancellation, billing retry, refund and plan changes. Modelling that yourself means maintaining two state machines indefinitely, both of which can change with every store generation. In Klaimo, RevenueCat handles that mediation, so a single cross-platform subscription status exists — at the price of a third party in the payment path and ongoing, usage-based costs.

How does data from Garmin devices get into the app?

Through the Garmin API, once the relevant account has been linked. The important thing is the right expectation: third-party platforms deliver data on their own schedule and in their own data model, not as a live stream on demand. In practice that means matching activities reliably, avoiding duplicates, and building the app so it still behaves sensibly when external data arrives late or not at all.

Can this architecture be applied to other products?

The pattern holds wherever derived results are produced continuously from raw or sensor data: a lean app for capture and display, a separate stateless compute service for the expensive operations, and rendering through vector layers instead of many individual objects. Whether you are processing geospatial data in real time, evaluating areas or aggregating large measurement series changes little about that split. What differs is mainly the domain logic inside the compute service.

Planning something similar?

SB-Techworks builds custom apps for iOS and Android – from MVP to scalable business app, straight from development. You get an honest assessment of feasibility and cost within 24 hours (Mon–Fri).

Request a free assessment  custom app development →