-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compute overall score #4
Comments
Maybe we could calculate a number similar to https://googlechrome.github.io/lighthouse/scorecalc/ |
Working example: import { getCLS, getFID, getLCP } from 'web-vitals'
import { createApiReporter } from 'web-vitals-reporter'
const report = createApiReporter('/analytics', {
beforeSend: (result) => {
const lcpScore = computeLogNormalScore({ p10: 2500, median: 4500 }, result.LCP)
const fidScore = computeLogNormalScore({ p10: 100, median: 300 }, result.FID || 0)
const clsScore = computeLogNormalScore({ p10: 0.1, median: 0.25 }, result.CLS)
const score = Math.min(lcpScore, fidScore, clsScore)
return { lcpScore, fidScore, clsScore, score }
},
})
getLCP(report)
getFID(report)
getCLS(report)
// helpers extracted from Lighthouse
// https://github.com/GoogleChrome/lighthouse/blob/b36b694f12b668cc64d232b8fe7c0507011bfdb7/lighthouse-core/audits/audit.js#L79
/** @param {{median: number, p10: number}} controlPoints @param {number} value */
function computeLogNormalScore(controlPoints, value) {
const percentile = getLogNormalScore(controlPoints, value)
return clampTo2Decimals(percentile)
}
/** @param {{median: number, p10: number}} parameters @param {number} value */
function getLogNormalScore({ median, p10 }, value) {
// Required for the log-normal distribution.
if (median <= 0) throw new Error('median must be greater than zero')
if (p10 <= 0) throw new Error('p10 must be greater than zero')
// Not required, but if p10 > median, it flips around and becomes the p90 point.
if (p10 >= median) throw new Error('p10 must be less than the median')
// Non-positive values aren't in the distribution, so always 1.
if (value <= 0) return 1
// Closest double to `erfc-1(2 * 1/10)`.
const INVERSE_ERFC_ONE_FIFTH = 0.9061938024368232
// Shape (σ) is `log(p10/median) / (sqrt(2)*erfc^-1(2 * 1/10))` and
// standardizedX is `1/2 erfc(log(value/median) / (sqrt(2)*σ))`, so simplify a bit.
const xLogRatio = Math.log(value / median)
const p10LogRatio = -Math.log(p10 / median) // negate to keep σ positive.
const standardizedX = (xLogRatio * INVERSE_ERFC_ONE_FIFTH) / p10LogRatio
const complementaryPercentile = (1 - erf(standardizedX)) / 2
// Clamp to [0, 1] to avoid any floating-point out-of-bounds issues.
return Math.min(1, Math.max(0, complementaryPercentile))
}
/** @param {number} x */
function erf(x) {
// erf(-x) = -erf(x);
const sign = Math.sign(x)
x = Math.abs(x)
const a1 = 0.254829592
const a2 = -0.284496736
const a3 = 1.421413741
const a4 = -1.453152027
const a5 = 1.061405429
const p = 0.3275911
const t = 1 / (1 + p * x)
const y = t * (a1 + t * (a2 + t * (a3 + t * (a4 + t * a5))))
return sign * (1 - y * Math.exp(-x * x))
}
/** @param {number} val */
function clampTo2Decimals(val) {
return Math.round(val * 100) / 100
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Would it be possible for this lib to compute a overall score (like the lighthouse perf score)?
I want to integrate the low level metrics into a cms, but it would help for a top level view if there was a single score at first. The user would then „drill down“ into the separate metrics
The text was updated successfully, but these errors were encountered: