A client-side React application that visualizes Google Health sleep data in a way that's helpful for people with non-24-hour sleep-wake disorder (N24). The main feature is an actogram with sleep stage data (when available), and an overlay that displays estimated circadian night on each day, calculated based on the entire visible data set.
In many aspects inspired by fitbit-sleep-vis as I've been using it for years, but this project is written completely from scratch due to vastly different objectives and implementation besides fetching.
This repository is a reimplementation of fitbit-n24 for Google Health API as Fitbit API will be deprecated this year.
Visualization
- Canvas actogram with sleep stage coloring, one row per day
- Periodogram for circadian frequency analysis
- Optional double-plot mode for patterns crossing row boundaries
- Adjustable row height
- Adjustable row width to match the data's circadian period, allowing sleep records to line up
- Interactive tooltips with date, times, duration, efficiency, stage breakdown
Circadian analysis
- Estimated circadian night overlay (purple) computed from sleep data
- Forecast circadian night 2/7/30 days ahead using recent trend
- Schedule overlay (green) to compare circadian night against weekly commitments
Data
- Fetch from Google Health API with progressive loading and IndexedDB caching
- Import/export JSON
- Date range filter for all visualizations and calculations
- Export what you see as PNG
| Layer | Choice |
|---|---|
| Build | Vite |
| UI | React 19 + TypeScript |
| CSS | Tailwind CSS v4 |
| Visualization | HTML Canvas + d3-scale |
| Auth | OAuth 2.0 PKCE |
npm install
npm run devOpen http://localhost:5173.
To fetch data directly from the Google Health API:
- Create OAuth credentials in Google Cloud Console
- OAuth client type: Web application
- Authorized redirect URI:
http://localhost:5173/
- Copy
.env.exampleto.envand fill in your client ID:VITE_GOOGLE_HEALTH_CLIENT_ID=your_client_id_here VITE_GOOGLE_HEALTH_CLIENT_SECRET=your_client_secret_here - Start the dev server and click "Sign in"
The app starts empty. You can:
- Fetch from Google Health: Sign in via OAuth and fetch all historical sleep data. Data is cached in IndexedDB; subsequent fetches only retrieve new records.
- Import JSON: Load a previously exported file or raw API response data.
- Import Google Takeout data: Multiple sleep-YYYY-MM-DD.json files from Takeout can be imported at the same time.
- Export JSON: Save the current dataset as a JSON file that can be re-imported later.
Supported import formats:
- Google Health sleep dataPoints
- Fitbit API v1.2 response
- Previously exported data from this app
src/
main.tsx Entry point, provider hierarchy
App.tsx Layout shell (no logic)
AppContext.tsx Global state, derived values, context provider
index.css Tailwind directives + global styles
api/ API types, fetch wrappers, paging implementations
auth/ OAuth 2.0 PKCE flow, auth context provider
data/ Data loading, IndexedDB caching, fetch orchestration
models/ Actogram row transform, circadian estimation, sleep scoring, periodogram
components/ UI components (header, toolbar, controls, actogram, periodogram, legend, schedule editor)
