{"id":26821560,"url":"https://github.com/mattlean/sleep-score","last_synced_at":"2026-05-16T06:38:18.675Z","repository":{"id":273492162,"uuid":"919284135","full_name":"mattlean/sleep-score","owner":"mattlean","description":"Custom sleep score algorithm","archived":false,"fork":false,"pushed_at":"2025-03-28T03:05:42.000Z","size":140,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-31T05:56:57.076Z","etag":null,"topics":["apps-script","google-sheets","javascript","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mattlean.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-20T05:33:05.000Z","updated_at":"2025-03-28T03:05:46.000Z","dependencies_parsed_at":"2025-03-28T04:29:47.103Z","dependency_job_id":null,"html_url":"https://github.com/mattlean/sleep-score","commit_stats":null,"previous_names":["mattlean/sleep-score"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mattlean/sleep-score","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattlean%2Fsleep-score","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattlean%2Fsleep-score/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattlean%2Fsleep-score/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattlean%2Fsleep-score/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattlean","download_url":"https://codeload.github.com/mattlean/sleep-score/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattlean%2Fsleep-score/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33092830,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apps-script","google-sheets","javascript","nodejs"],"created_at":"2025-03-30T07:31:18.750Z","updated_at":"2026-05-16T06:38:18.656Z","avatar_url":"https://github.com/mattlean.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sleep Score\n\nThis is a fun little personal sleep score project made to help improve my sleep.\n\nI've always had a really hard time getting to sleep and staying asleep due to health reasons, so I require a lot more effort in getting a good night's rest when compared to normal people.\n\nThis algorithm uses data collected by a sleep tracking device to calculate sleep scores based on user set goals. This calculation can be run for a particular day, week, month, etc. It is generic, so it isn't tied to any specific brand of device, but I personally use a Fitbit. It only requires the device that tracks bedtime, time asleep, and sleep stage durations.\n\nAt the moment the calculation is performed through [Google Sheets](https://workspace.google.com/products/sheets) using [Apps Script](https://developers.google.com/sheets), but the code can be easily adapted to work without it. All of the Apps Script-specific functionality is separated into its own file in [`gsheets.gs`](./src/gsheets.gs).\n\nFitbit already has a sleep score feature that works well for most people, but I find it a bit too generous for my scenario, although that makes sense since it needs to work for an extremely large demographic.\n\nUnfortunately I don't currently have what is considered to be a normal sleep pattern, so I need a sleep score that follows a stricter ruleset while allowing for adjustments each time the calculation is run. The way the score works may potentially stress out some people, but I am not usually troubled by bad health metrics. Personally, I feel more motivated to improve when I understand them.\n\n# Calculation\n\n## Parameters\n\n### Define Initial Parameters\n\nThe goals act as the initial parameters for the calculation.\n\n#### Bedtime Goal\n\nSet the earliest possible bedtime that would be considered excellent for your goals. This time is referred to as the **bedtime goal**.\n\nThe algorithm will use this bedtime goal to generate ranges of time that will be used to grade the bedtimes we have. Generally speaking, the closer the bedtime is to the bedtime goal, the better grade we will get.\n\nWe will use 9:00 PM as the bedtime goal for our example scenario.\n\n#### Time Asleep Thresholds\n\nSet the minimum thresholds for the excellent, fair, and poor time asleep.\n\nA time asleep meeting or exceeding the excellent threshold is considered an excellent time asleep.\n\nA time asleep meeting or exceeding the fair threshold, but is still under the excellent threshold is considered a fair time asleep.\n\nA time asleep meeting or exceeding the poor threshold, but is still under the fair threshold is considered a poor time asleep.\n\nWe will use the following as our time asleep goals for our example:\n\n| Grade     | Example | Window of Duration |\n| --------- | ------- | ------------------ |\n| Excellent | 9 hours | \u003e 9:00             |\n| Fair      | 7 hours | 7:00 – 8:59        |\n| Poor      | 3 hours | 3:00 – 6:59        |\n\nNote that we reasoned to use 3 hours as the threshold for the poor grade because Fitbit does not track sleep stages when the time asleep for a session is under 3 hours.\n\n#### Deep Sleep Percentage Goal\n\nSet the minimum percentage of deep sleep for each session that would be considered excellent for your goals.\n\nWe'll use 15% for this example.\n\n#### REM Sleep Percentage Goal\n\nSet the minimum percentage of REM sleep for each session that would be considered excellent for your goals.\n\nWe'll use 20% for this example.\n\n### Record Sleep Metric Parameters\n\nNow that we have our goals defined, all we need to do is sleep and gather metrics for a session.\n\nThese are the actual measurements taken from a particular sleep session that will be evaluated:\n\n- **Bedtime**: When you feel asleep\n- **Time asleep**: How long you stayed asleep\n- **Deep sleep**: How long you stayed in deep sleep\n- **REM sleep**: How long you stayed in REM sleep\n\n## Formula\n\nNow that we have both our initial and sleep metric parameters ready, we can start the calculation for a sleep score by working through this formula:\n\n$$\\left\\lceil S \\right\\rceil = (T \\times 0.6 + D \\times 0.4) \\times 0.8 + Q \\times 0.2$$\n\n## Grading $T$ (Bedtime Grade)\n\n$T$ represents the **bedtime grade** which uses the bedtime metric from the given sleep session and evaluates it against grading ranges generated from your bedtime goal.\n\nContinuing the example we've been working with, let's see what the grading ranges are when the bedtime goal is 9:00 PM and actually grade a 9:30 PM bedtime metric against it:\n\n| Grade     | Window of Time (Offset From Bedtime Goal)    | 9:00 PM Bedtime Goal Example | Points |\n| --------- | -------------------------------------------- | ---------------------------- | ------ |\n| Fair      | -0.5 to 0 hours                              | 8:30 PM – 8:59 PM            | +90    |\n| Excellent | +0 to 2.75 hours                             | 9:00 PM – 11:45 PM           | +100   |\n| Fair      | +2.75 to 3.5 hours                           | 11:46 PM – 12:30 AM          | +90    |\n| Bad       | Any time outside of excellent \u0026 fair windows | 12:31 AM – 8:29 PM           | +50    |\n| Fail      | No bedtime                                   | N/A                          | +0     |\n\n_The maximum amount of possible points earned is 100._\n\n9:30 PM is considered an excellent bedtime, resulting to $T = 100$.\n\n## Grading $D$ (Time Asleep Grade)\n\n$D$ represents the **time asleep grade** which uses the time asleep metric from the given sleep session and evaluates it against your time asleep thresholds.\n\nLet's say our time asleep metric is 7 hours and grade it against our previously defined thresholds:\n\n| Grade     | Goal Example | Window of Duration | Points |\n| --------- | ------------ | ------------------ | ------ |\n| Excellent | 9 hours      | \u003e= 9:00            | +100   |\n| Fair      | 7 hours      | 7:00 – 8:59        | +70    |\n| Poor      | 3 hours      | 3:00 – 6:59        | +60    |\n| Bad       | \u003c3 hours     | 0:01 – 2:59        | +50    |\n| Fail      | N/A          | No time asleep     | +0     |\n\n_The maximum amount of possible points earned is 100._\n\n7 hours is considered a fair time asleep, resulting to $D = 70$.\n\n## Grading $Q$ (Quality of Sleep Grade)\n\n$Q$ represents the **quality of sleep grade** which uses the deep and REM sleep metrics from the given sleep session and evaluates it against thresholds generated from your deep and REM sleep percentage goals.\n\nBefore we can calculate $Q$, we need to grade deep and REM sleep as $d$ and $r$ respectively.\n\n### Grading $d$ (Deep Sleep Grade)\n\nWhen the deep sleep goal percentage is 15%, 15% of excellent time asleep determines the threshold for excellent deep sleep. 15% of fair time asleep determines the threshold for fair deep sleep.\n\nLet's say our deep sleep metric is 1 hour and grade it against thresholds generated using 15%:\n\n| Grade     | Window Duration | Points |\n| --------- | --------------- | ------ |\n| Excellent | \u003e= 1:21         | +50    |\n| Fair      | 1:03 – 1:20     | +40    |\n| Bad       | 0:01 – 1:02     | +10    |\n| Fail      | No deep sleep   | +0     |\n\n_The maximum amount of possible points earned is 50._\n\n1 hour is considered bad sleep, so $d = 10$.\n\n### Grading $r$ (REM Sleep Grade)\n\nWhen the REM sleep goal percentage is 20%, 20% of excellent time asleep determines the threshold for excellent REM sleep. 20% of fair time asleep determines the threshold for fair REM sleep.\n\nLet's say our REM sleep metric is 1.5 hours and grade it against thresholds generated using 20%:\n\n| Grade     | Window Duration | Points |\n| --------- | --------------- | ------ |\n| Excellent | \u003e= 1:48         | +50    |\n| Fair      | 1:24 – 1:47     | +40    |\n| Bad       | 0:01 – 1:23     | +10    |\n| Fail      | No REM sleep    | +0     |\n\n_The maximum amount of possible points earned is 50._\n\n1.5 hours is considered fair sleep, so $r = 40$.\n\n### Calculating $Q$\n\nNow that we have $d$ and $r$, all we need to do is sum them together to get $Q$.\n\n$$Q = d + r$$\n\nFor our example, that means that $Q = 50$.\n\nWhen used with Fitbit, if there is no data available for deep and REM sleep, we will assume that the tracker did not track sleep stage data because the time asleep was under 3 hours resulting to $Q = 0$.\n\n## Calculating $S$ (Sleep Score)\n\nNow that we have calculated all of the necessary grades, we simply plug them into the formula and compare $S$ against the following grading scale:\n\n| Grade     | Score Range |\n| --------- | ----------- |\n| Excellent | 100         |\n| Good      | 90 – 99     |\n| Fair      | 80 – 89     |\n| Poor      | 70 – 79     |\n| Bad       | 1 – 69      |\n| Fail      | 0           |\n\nTo finish off our example, here is what the formula looks like:\n\n$$\\left\\lceil 80.4 \\right\\rceil = (100 \\times 0.6 + 70 \\times 0.4) \\times 0.8 + 50 \\times 0.2$$\n\nAfter taking the ceiling of $Q$, we get a sleep score of $81$ which is considered a fair sleep score.\n\n## Potential Improvements\n\n- Factor in sleep debt like [Whoop](https://support.whoop.com/s/article/WHOOP-Sleep).\n- Punish an excessive amount of days with oversleep (10+ hours).\n- Factor in sleep efficiency: time asleep percentage of total time in bed, and number of sleep sessions.\n- Factor in heart rate like [Fitbit's restoration metric](https://support.google.com/fitbit/answer/14236513?hl=en#zippy=%2Chow-is-my-sleep-score-calculated-in-the-fitbit-app) under quality of sleep.\n- Incorporate elements commonly used in readiness and recovery scores like HRV.\n\n# Built with Lean JS App Starter\n\nThis was created with [Lean JavaScript Application Starter](https://github.com/mattlean/lean-js-app-starter).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattlean%2Fsleep-score","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattlean%2Fsleep-score","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattlean%2Fsleep-score/lists"}