projects/jalan
$ cat projects/jalan/README.md

Jalan

Record every bus and MRT ride, see each journey on the map, and find your fastest and cheapest commute.

status
beta
url
jalan.how ↗
started
09/2026
stack
Go · SQLite/Turso · Hotwire · LTA DataMall + OneMap
Jalan's Ride tab and a finished journey from Tampines to Telok Ayer on the map
fig.1 — the Ride tab, and a finished journey drawn on the map

## why

As someone who takes public transport in Singapore every day, I find the buses and MRT here very satisfactory, except for a few very niche parts.

  • I cannot see how much a trip actually costs in real time. When using credit cards, the total fare for the whole day is aggregated and only shows up the next day. With EZ-Link cards, you can see a preview of the fare when tapping in on the bus or at the MRT gantry, but it’s only for a brief moment. Checking the details in the SimplyGo app doesn’t help either, since it usually only shows the aggregated data a couple of days later.
  • When commuting between home & work, there are several public transport options available, which vary from bus-to-bus transfers to a couple of bus-to-train transfers via different train stations. And I can never really compare them in terms of speed & cost.

This is why I started working on Jalan (which literally means “go” in Malay/Indonesian). It’s not really a public transport planner, but more of a logger/tracker. Use it for several days during your day-to-day commute, and it can give you a summary of how much you actually spent and which routes are the best for your routine trips.

Fun fact: the domain is jalan.how, a play on the Singlish “Jalan how?”, which is exactly what the app is trying to answer.

## how it works

  1. The Ride tab asking "What are you boarding?" with Bus and MRT, beside the bus screen listing the next arrivals at the nearest stop
    01 pick your ride Tap Bus to see the nearest stops with their next arrivals and how full each bus is, or MRT for the lines at the nearest stations. The first ride starts a journey, later ones are transfers. Each tap saves the time and a location fix.
  2. A journey from Tampines to Telok Ayer on the map, with its bus and train rides listed by time
    02 see it on the map Every ride is matched to the stop or station it started at, and the route is drawn from LTA DataMall data on top of OneMap, Singapore's official map.
  3. The Journeys tab listing finished journeys by day, each with its estimated fare and the day's total
    03 know the fare When a journey ends it's priced at the adult card fare, with rides grouped into fare journeys by the transfer rules. If a stop or time was wrong, adjust the journey or add a ride you forgot, and the fare is worked out again.
  4. The Home to Work commute page with its routes on the map, the typical time, fare and departure, and how often each route was taken
    04 learn your commute Tag journeys like Home to Work and each commute gets its own page with the typical time, fare and departure, the routes you take and how the trip time trends. After three trips, a journey in progress shows roughly how long is left.

## stack

Go net/http + html/template// backend
The Go server renders every page as plain HTML with Go's standard library.
SQLite / Turso// database
SQLite for fast iteration in development; Turso, a hosted SQLite, in production.
LTA DataMall// data
Bus stops, bus routes, MRT/LRT stations and fares come from Singapore's Land Transport Authority DataMall. The bus arrivals is fetched live from LTA API.
Turbo + Stimulus// frontend
They avoid full page reloads, and they keep the app one long-lived page, so the phone asks for location once per launch instead of on every screen.
Leaflet + OneMap// maps
OneMap is Singapore's official map, used both as the basemap and to draw bus rides along their real route. Leaflet only loads on pages that show a map.
Fly.io// infra
Cheap, and friendly to both users and agents.

$ git log --oneline --all

  1. docs/new-regionsplanned

    More regions outside SG to support: Japan, Jakarta, Auckland and Wellington.

  2. HEAD → main2026-10-05

    Pages swap in place with Turbo; the app's JavaScript is split into Stimulus controllers

    Now that the app has settled into its final shape, this version makes it snappier: interactions no longer re-render the whole page.

  3. 2026-10-03

    Adjust a finished journey's stops and times

    For when you forget to tap during a journey: fix the boarding times, or add a missing transfer between rides.

  4. 2026-09-30

    Commutes: tag journeys and see what each trip is usually like

    Journeys can be tagged with a commute (e.g. Home → Work); the app then compares the routes and shows which is fastest and cheapest.

  5. 2026-09-29

    Bus picker with live arrivals

    New quality-of-life feature for the bus picker: shows each bus's estimated arrival time and how crowded it is, using LTA's Bus Arrival API.

  6. 2026-09-29

    Fares estimated automatically when a journey ends

    Each tap is saved with a GPS fix, which is matched against LTA DataMall's open data to work out the route, the distance travelled and the estimated fare.

  7. 2026-09-28 · initial commit

    Record bus and MRT journeys

    The first MVP: the browser's location API records where you are at each tap, and OneMap draws the finished journeys.