A lightweight workout tracker that lets you log jogging and biking workouts on an interactive map. Trackr uses your browser’s geolocation to center the map, lets you click to add a workout at a location, and persists workouts in Local Storage so they’re still there when you refresh.
Built with vanilla JavaScript + HTML/CSS, bundled with Parcel.
- Demo
- Features
- How it works
- Tech stack
- Project structure
- Getting started
- Prerequisites
- Install
- Run locally
- Build for production
- Usage guide
- Create a workout
- Delete a workout
- Delete all workouts
- Data model
- Persistence
- Configuration / customization
- Troubleshooting
- Contributing
- License
- Authors
- Deployed site (as referenced in HTML meta tags):
https://trackrie.netlify.app
If you fork/host your own version, update og:url and other metadata in index.html.
- Interactive map powered by Leaflet + OpenStreetMap tiles
- Geolocation-based start position
- Log 2 workout types:
- Jogging: distance, duration, cadence → pace is computed
- Biking: distance, duration, elevation gain → speed is computed
- Click-to-add workflow:
- click map → form appears
- submit workout → marker + list item are rendered
- Workout list sidebar:
- click a workout → map pans/zooms to its marker
- Delete single workout (via the
×button on a workout list item) - Delete all workouts
- Persistence with Local Storage (
localStorage["workouts"])
- On page load,
App:- requests geolocation permissions
- loads saved workouts from Local Storage
- renders saved workouts into the sidebar
- After geolocation is granted, Leaflet initializes the map and:
- listens for map clicks
- renders markers for any saved workouts
- When you submit the form:
- Trackr validates numeric inputs
- creates a
JoggingorBikingobject (both extendWorkout) - renders the workout in the list
- renders a popup marker on the map
- saves the updated workout list to Local Storage
Core
- JavaScript (vanilla)
- HTML / CSS
Libraries
- Leaflet (loaded from
unpkg.com) for maps - OpenStreetMap tiles (via Leaflet tile layer URL)
- Google Fonts (Manrope)
Tooling
- Parcel bundler (v1 via
parcel-bundler) - Prettier config included at
trackr/.prettierrc
At the repository root:
index.html– application shell (loads Leaflet + app assets)trackr/scripts.js– application logic (classes + DOM + Leaflet + persistence)trackr/styles.css– stylingtrackr/public/trackrlogo.png– app logo used in the sidebartrackr/tractr-architecture-files/– architecture diagrams/imagespackage.json– Parcel scripts
- Node.js + npm installed
- A modern browser with Geolocation support (Chrome/Edge/Firefox/Safari)
Note: Geolocation often requires
httpsin production. Locally,http://localhostis typically allowed.
git clone https://github.com/Ebendttl/trackr.git
cd trackr
npm installnpm startParcel will start a dev server. Open the URL printed in your terminal (commonly http://localhost:1234 when using Parcel v1).
npm run buildThis generates a production build (Parcel output directory depends on Parcel settings; by default it emits to dist/).
- Allow location access when prompted.
- Click anywhere on the map.
- Fill out the form:
- Select Jogging or Biking
- Enter distance (km) and duration (min)
- If Jogging: enter cadence (step/min)
- If Biking: enter elevation gain (meters)
- Press Enter / submit the form.
You’ll see:
- a marker at the clicked location
- a workout card in the sidebar list
- Click the × in the workout card header.
Current implementation reloads the page after deletion to refresh the UI.
- Click Delete All at the bottom of the sidebar.
This removes the workouts key from Local Storage and reloads the page.
Workouts are modeled using classes in trackr/scripts.js:
Workoutdate(Date)id(derived from timestamp)coords([lat, lng])distance(km)duration(min)description(generated, e.g., “Jogging on March 17”)
Jogging extends Workouttype = "jogging"cadence(step/min)pace(min/km) calculated asduration / distance
Biking extends Workouttype = "biking"elevationGain(meters)speed(km/h) calculated asdistance / (duration / 60)
Trackr uses the browser’s Local Storage API:
- Save:
localStorage.setItem("workouts", JSON.stringify(workoutsArray)) - Load:
JSON.parse(localStorage.getItem("workouts"))
Because objects are serialized/deserialized as plain JSON, loaded workouts do not regain their class prototypes. (The app currently only needs the stored fields for rendering, so this works fine.)
A few easy tweaks you can make:
- Map zoom level: update
#mapZoomLevelinApp - Tile layer: replace the Leaflet
tileLayerURL (currently OpenStreetMap) - Branding:
- Sidebar logo:
trackr/public/trackrlogo.png - Open Graph image:
/trackrlogo1.png(root file)
- Sidebar logo:
- Workout types:
- Add a new subclass of
Workout - Update the form in
index.html - Add rendering logic in
_renderWorkout()and marker popup content
- Add a new subclass of
- Ensure location permissions are enabled for the site.
- Try running on
http://localhost(allowed by most browsers). - If deployed, use
https.
- Confirm Leaflet CSS/JS CDN links are reachable.
- Check the browser console for network errors.
- Local Storage can be cleared by:
- browser settings
- private/incognito mode
- extensions that clear site data
Contributions are welcome.
- Fork the repo
- Create a branch:
git checkout -b my-feature
- Commit:
git commit -m "Add feature: ..." - Push:
git push origin my-feature
- Open a Pull Request
This project is currently marked as ISC in package.json.
If you want it to be MIT (the previous README mentioned MIT), update package.json and add a LICENSE file to make it explicit.
- Fakolujo Micheal Ayomide (
@thefaks_officia) - Akinseinde Ebenezer Akindele (
@Ebendttl)