]> git.basschouten.com Git - sp3dashboard.git/commitdiff
Put SP3 dashboard code in git.
authorBas Schouten <bschouten@mozilla.com>
Wed, 5 Jun 2024 11:13:14 +0000 (11:13 +0000)
committerBas Schouten <bschouten@mozilla.com>
Wed, 5 Jun 2024 11:13:14 +0000 (11:13 +0000)
details-jetstream.html [new file with mode: 0644]
details-sp2.html [new file with mode: 0644]
details-subtests.html [new file with mode: 0644]
details.html [new file with mode: 0644]
diff-overlay.html [new file with mode: 0644]
diff.html [new file with mode: 0644]
index.html [new file with mode: 0644]

diff --git a/details-jetstream.html b/details-jetstream.html
new file mode 100644 (file)
index 0000000..bbd21a6
--- /dev/null
@@ -0,0 +1,422 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Speedometer 3 Status Dashboard</title>
+
+  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
+
+  <script>
+    function round(number, decimals) {
+      return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
+    }
+
+    var platform = 'windows10-64-shippable-qr';
+
+    const searchParams = new URLSearchParams(window.location.search);
+
+    let likelyDataURL = 'https://sql.telemetry.mozilla.org/api/queries/92092/results.json?api_key=MaQkSpIR19BM4Jr5QPKt7nDZpkjEPmmGc2cEtpiW';
+    let timeWindow = '6wk';
+
+    if (searchParams.get('os') == 'osx') {
+      platform = 'macosx1015-64-shippable-qr';
+    } else if (searchParams.get('os') == 'linux') {
+      platform = 'linux1804-64-shippable-qr';
+    } else if (searchParams.get('os') == 'android') {
+      platform = 'android-hw-a51-11-0-aarch64-shippable-qr';
+    }
+
+    fetch(likelyDataURL)
+      .then(function (response) {
+        return response.json();
+      })
+      .then(function (data) {
+        loadDataLikely(data);
+      })
+      .catch(function (err) {
+        console.log('error: ' + err);
+      });
+
+    function getDiffColor(diff) {
+      if (diff > 0.05) {
+        return 'green'
+      } else if (diff > -0.095) {
+        return 'black'
+      } else if (diff > -0.2) {
+        return '#b38f00'
+      } else {
+        return 'red'
+      }
+    }
+
+    function getSelfDiffColor(diff) {
+      if (diff > 1.02) {
+        return 'green';
+      } else if (diff < 0.98) {
+        return 'red';
+      } else {
+        return 'black';
+      }
+    }
+
+    function fillData(tableId, row) {
+      if (row.platform != platform) {
+        return;
+      }
+      diffColor = getDiffColor(row.Difference);
+      oldDiffColor = getDiffColor(row.OldDifference);
+
+      let firefoxDiff = 1.0;
+      if (row.FirefoxOldMean > 0) {
+        firefoxDiff = row.FirefoxDifference;
+      }
+
+      document.getElementById(tableId).innerHTML += '<tr onClick="openTestComparison(' + row.signature_id_firefox + ', ' + row.signature_id_chrome + ');"><th scope="row" class="testName">' +
+        row.test.split('/')[0] + '</th><td>' +
+        round(row.FirefoxMean, 2) + ' runs/s</td><td>' +
+        round(row.ChromeMean, 2) + ' runs/s</td><td style=\'color: ' + diffColor + '\'>' +
+        Math.round(-row.Difference * 100) + '\%</td><td>' +
+        round(row.FirefoxOldMean, 2) + ' runs/s</td><td>' +
+        round(row.ChromeOldMean, 2) + ' runs/s</td><td style=\'color: ' + oldDiffColor + '\'>' +
+        Math.round(-row.OldDifference * 100) + '\%</td><td style=\'color: ' + getSelfDiffColor(firefoxDiff) + '\'>' +
+        Math.round(-(1 - firefoxDiff) * 100) + '%</td></tr >\n';
+    }
+    function loadDataLikely(data) {
+      data.query_result.data.rows.forEach(fillData.bind(null, 'tableBodyLikely'));
+
+      document.getElementById('timewindow').innerHTML = timeWindow + ' prior';
+
+      let arithmeticMeanOfLogsFX = 0;
+      let arithmeticMeanOfLogsFXOld = 0;
+      let arithmeticMeanOfLogsChrome = 0;
+      let arithmeticMeanOfLogsChromeOld = 0;
+      let numValues = 0;
+      let numValuesOld = 0;
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform != platform) {
+          return;
+        }
+        numValues++;
+        arithmeticMeanOfLogsFX += Math.log(row.FirefoxMean);
+        arithmeticMeanOfLogsChrome += Math.log(row.ChromeMean);
+        if (row.FirefoxOldMean > 0) {
+          numValuesOld++;
+          arithmeticMeanOfLogsFXOld += Math.log(row.FirefoxOldMean);
+          arithmeticMeanOfLogsChromeOld += Math.log(row.ChromeOldMean);
+        }
+      });
+      let FxGeoMean = Math.exp(arithmeticMeanOfLogsFX / numValues);
+      let ChromeGeoMean = Math.exp(arithmeticMeanOfLogsChrome / numValues);
+      let FxOldGeoMean = Math.exp(arithmeticMeanOfLogsFXOld / numValuesOld);
+      let ChromeOldGeoMean = Math.exp(arithmeticMeanOfLogsChromeOld / numValuesOld);
+
+      let diff = FxGeoMean / ChromeGeoMean - 1;
+      let oldDiff = ChromeOldGeoMean > 0 ? (FxOldGeoMean / ChromeOldGeoMean - 1) : 0;
+      let diffColor = getDiffColor(diff);
+      let oldDiffColor = getDiffColor(oldDiff);
+      let firefoxDiff = 0;
+      if (FxOldGeoMean > 0) {
+        firefoxDiff = FxGeoMean / FxOldGeoMean;
+      }
+
+      document.getElementById("tableBodyLikely").innerHTML += '<tr class="sumRow"><th scope="row" class="testName">Total Score</th><td>' +
+        round(FxGeoMean, 2) + ' runs/s</td><td>' +
+        round(ChromeGeoMean, 2) + ' runs/s</td><td style=\'color: ' + diffColor + '\'>' +
+        round(-diff * 100, 2) + '\%</td><td>' +
+        round(FxOldGeoMean, 2) + ' runs/s</td><td>' +
+        round(ChromeOldGeoMean, 2) + ' runs/s</td><td style=\'color: ' + oldDiffColor + '\'>' +
+        round(-oldDiff * 100, 2) + '\%</td><td style=\'color: ' + getSelfDiffColor(firefoxDiff) + '\'>' +
+        Math.round(-(1 - firefoxDiff) * 100) + '%</td></tr >\n';
+    }
+
+    function openTestComparison(signatureFirefox, signatureChrome) {
+      let repoFirefox = 'mozilla-central';
+      let repoChrome = 'mozilla-central';
+      if (platform == 'android-hw-a51-11-0-aarch64-shippable-qr') {
+        repoFirefox = 'firefox-android';
+      }
+      window.open("https://treeherder.mozilla.org/perfherder/graphs?highlightAlerts=1&highlightChangelogData=1&highlightCommonAlerts=0&series=" + repoFirefox + "," + signatureFirefox + ",1,13&series=" + repoChrome + "," + signatureChrome + ",1,13&timerange=7776000", "_blank")
+    }
+
+    function loadChart() {
+      if (searchParams.get('os') == 'osx') {
+        document.getElementById("macbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'linux') {
+        document.getElementById("linuxbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'android') {
+        document.getElementById("mobilebutton").style.backgroundColor = "gray";
+      } else {
+        document.getElementById("windowsbutton").style = "background-color:gray;";
+      }
+      fetch('https://sql.telemetry.mozilla.org/api/queries/89971/results.json?api_key=6cd9JzffuVjIytWO8fuXs33HXKRjiy9bHJmHfndj')
+        .then(function (response) {
+          return response.json();
+        })
+        .then(function (data) {
+          displayChart(data);
+        })
+        .catch(function (err) {
+          console.log('error: ' + err);
+        });
+    }
+
+    function displayChart(data) {
+      const ctx = document.getElementById('myChart').getContext('2d');
+      let xyValuesFx = [];
+      let xyValuesChrome = [];
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform != platform) {
+          return;
+        }
+        // Hack to remove values where a run failed.
+        if (row.application == 'firefox' && row.geomean > 300) {
+          return;
+        }
+        if (row.application == 'firefox' || row.application == 'fenix') { xyValuesFx.push({ x: row.push_timestamp, y: row.geomean }); }
+        if (row.application == 'chrome' || row.application == 'chrome-m') { xyValuesChrome.push({ x: row.push_timestamp, y: row.geomean }); }
+      });
+
+      let cfg = {
+        type: "scatter",
+        data: {
+          datasets: [
+            {
+              label: 'Firefox',
+              pointRadius: 4,
+              pointBackgroundColor: "#FF9500",
+              data: xyValuesFx
+            },
+            {
+              label: 'Chrome',
+              pointRadius: 4,
+              pointBackgroundColor: "#1DA462",
+              data: xyValuesChrome
+            }
+          ]
+        },
+        options: {
+          aspectRatio: 2,
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              }
+            },
+            y: {
+              beginAtZero: true
+            }
+          },
+          plugins: {
+            annotation: {
+              annotations: {
+                line1: {
+                  type: 'line',
+                  xMin: '2023-03-01 03:56',
+                  xMax: '2023-03-01 03:56',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label1: {
+                  type: 'label',
+                  xValue: '2023-03-01 03:56',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 300 : 50,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['React-Stockcharts Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line2: {
+                  type: 'line',
+                  xMin: '2023-03-15 08:00',
+                  xMax: '2023-03-15 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label2: {
+                  type: 'label',
+                  xValue: '2023-03-15 08:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 300 : 50,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Editor Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line3: {
+                  type: 'line',
+                  xMin: '2023-03-29 08:00',
+                  xMax: '2023-03-29 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label3: {
+                  type: 'label',
+                  xValue: '2023-03-29 08:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 200 : 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Frameworks Updated'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line4: {
+                  type: 'line',
+                  xMin: '2023-04-14 08:00',
+                  xMax: '2023-04-14 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label4: {
+                  type: 'label',
+                  xValue: '2023-04-14 08:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 200 : 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Svelte Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                }
+              }
+            }
+          }
+        }
+      };
+
+      if (platform == 'linux1804-64-shippable-qr') {
+        cfg.options.plugins.annotation.annotations.line3 = {
+          type: 'line',
+          xMin: '2023-02-08 17:00',
+          xMax: '2023-02-08 17:00',
+          borderColor: 'black',
+          borderWidth: 2,
+        };
+        cfg.options.plugins.annotation.annotations.label3 = {
+          type: 'label',
+          xValue: '2023-02-08 17:00',
+          yValue: 50,
+          backgroundColor: 'rgb(255,128,128)',
+          content: ['Linux Firmware Update'],
+          font: {
+            size: 14
+          },
+          rotation: -90,
+          borderRadius: 4
+        };
+      }
+
+      let myChart = new Chart("myChart", cfg);
+    }
+
+  </script>
+  <style>
+    h3 {
+      font-family: sans-serif;
+    }
+
+    .styled-table {
+      border-collapse: collapse;
+      margin: 25px 0;
+      font-size: 0.9em;
+      font-family: sans-serif;
+      min-width: 400px;
+      box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+    }
+
+      .styled-table thead th {
+        background-color: #3366ff;
+        color: #ffffff;
+        text-align: center;
+      }
+
+      .styled-table th {
+        padding: 12px 15px;
+      }
+
+      .styled-table td {
+        text-align: right;
+      }
+
+      .styled-table .testName {
+        text-align: right;
+      }
+
+    .tableBody tr {
+      border-bottom: 1px solid #dddddd;
+    }
+
+    .tableBody .sumRow {
+      border-top: 2px solid black;
+    }
+
+    .tableBody tr:nth-of-type(odd) {
+      background-color: #f3f3f3;
+    }
+
+    .tableBody tr:nth-of-type(even) {
+      background-color: #e3e3e3;
+    }
+
+    .tableBody tr:hover {
+      background-color: #d0d0d0;
+    }
+
+    .buttons td {
+      width: 200px;
+      text-align: center;
+      font-family: sans-serif;
+      font-size: 20px;
+    }
+
+    a {
+      padding-left: 50px;
+      padding-right: 50px;
+      padding-top: 10px;
+      padding-bottom: 10px;
+      text-decoration: none;
+      color: black;
+      border-radius: 5px;
+    }
+
+      a:hover {
+        background-color: lightgray;
+      }
+  </style>
+</head>
+
+<body>
+  <center>
+    <table class="buttons">
+      <tr><td><a href="details-jetstream.html" id="windowsbutton">Windows</a></td><td><a href="details-jetstream.html?os=osx" id="macbutton">Mac OSX</a></td><td><a href="details-jetstream.html?os=linux" id="linuxbutton">Linux</a></td></tr>
+      <tr><td colspan="3" style="height:80px;"><a href="details-jetstream.html?os=android" id="mobilebutton">Android</a></td></tr>
+    </table>
+
+    <h3>Breakdown: Jetsream 2 Likely Candidates</h3>
+    <table class="styled-table">
+      <col>
+      <col>
+      <colgroup span="2"></colgroup>
+      <col>
+      <colgroup span="2"></colgroup>
+      <col>
+      <thead>
+        <tr><th rowspan="2"></th><th colspan="2" scope="colgroup">Total time (Higher is better)<br />1wk moving average</th><th rowspan="2">Difference</th><th colspan="2" scope="colgroup" id="timewindow">6wk prior</th><th rowspan="2">Difference</th><th rowspan="2">Firefox<br />Evolution</th></tr>
+        <tr><th scope="col">Firefox<br />Nightly</th><th scope="col">Chrome<br />Release</th><th scope="col">Firefox<br />Nightly</th><th scope="col">Chrome<br />Release</th></tr>
+      </thead>
+      <tbody class="tableBody" id="tableBodyLikely">
+      </tbody>
+    </table>
+  </center>
+</body>
+</html>
diff --git a/details-sp2.html b/details-sp2.html
new file mode 100644 (file)
index 0000000..2db7ce7
--- /dev/null
@@ -0,0 +1,448 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Speedometer 3 Status Dashboard</title>
+
+  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
+
+  <script>
+    function round(number, decimals) {
+      return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
+    }
+
+    var platform = 'windows10-64-shippable-qr';
+
+    const searchParams = new URLSearchParams(window.location.search);
+
+    let likelyDataURL = 'https://sql.telemetry.mozilla.org/api/queries/97531/results.json?api_key=IaOu0H5GXoCdMzhxudfQOMVVv66WgD1cfWpRYxk5';
+    let timeWindow = '6wk';
+    let supportsCaR = true;
+
+    let range = '';
+
+    var timeChart;
+
+    if (searchParams.get('os') == 'osx') {
+      platform = 'macosx1015-64-shippable-qr';
+    } else if (searchParams.get('os') == 'osxm2') {
+      platform = 'macosx1300-64-shippable-qr';
+    } else if (searchParams.get('os') == 'linux') {
+      platform = 'linux1804-64-shippable-qr';
+    } else if (searchParams.get('os') == 'android') {
+      platform = 'android-hw-a51-11-0-aarch64-shippable-qr';
+    }
+
+    fetch(likelyDataURL)
+      .then(function (response) {
+        return response.json();
+      })
+      .then(function (data) {
+        loadDataLikely(data);
+      })
+      .catch(function (err) {
+        console.log('error: ' + err);
+      });
+
+    function getDiffColor(diff) {
+      if (diff < -0.05) {
+        return 'green'
+      } else if (diff < 0.095) {
+        return 'black'
+      } else if (diff < 0.2) {
+        return '#b38f00'
+      } else {
+        return 'red'
+      }
+    }
+
+    function getSelfDiffColor(diff) {
+      if (diff <= 0.98) {
+        return 'green';
+      } else if (diff >= 1.02) {
+        return 'red';
+      } else {
+        return 'black';
+      }
+    }
+
+    function fillData(tableId, row) {
+      if (row.platform != platform) {
+        return;
+      }
+      diffColor = getDiffColor(row.Difference);
+      oldDiffColor = getDiffColor(row.OldDifference);
+
+      let firefoxDiff = 1.0;
+      if (row.FirefoxOldMean > 0) {
+        firefoxDiff = row.FirefoxDifference;
+      }
+
+      
+      let newLine = '';
+      newLine += '<tr onClick="openTestComparison(' + row.signature_id_firefox + ', ' + row.signature_id_chrome + ', ' + row.signature_id_car + ');"><th scope="row" class="testName">' +
+        row.test.split('/')[0] + '</th><td>' +
+        round(row.FirefoxMean, 2) + ' ms</td><td>' +
+        round(row.ChromeMean, 2) + ' ms</td>';
+      if (supportsCaR) {
+        newLine += '<td>' + round(row.CaRMean, 2) + ' ms</td>';
+      }
+      newLine += '<td style =\'color: ' + diffColor + '\'>' +
+        Math.round(-row.Difference * 100) + '\%</td>'
+      if (supportsCaR) {
+        newLine += '<td style=\'color: ' + getDiffColor(row.CaRDifference) + '\'>' + Math.round(-row.CaRDifference * 100) + '\%</td>'
+      }
+      newLine += '<td>' +
+        round(row.FirefoxOldMean, 2) + ' ms</td><td>' +
+        round(row.ChromeOldMean, 2) + ' ms</td><td style=\'color: ' + oldDiffColor + '\'>' +
+        Math.round(-row.OldDifference * 100) + '\%</td><td style=\'color: ' + getSelfDiffColor(firefoxDiff) + '\'>' +
+        Math.round(-(1 - firefoxDiff) * 100) + '%</td></tr >\n';
+
+      document.getElementById(tableId).innerHTML += newLine;
+    }
+
+    function loadDataLikely(data) {
+      let table = document.getElementById('tableLikely');
+      if (!supportsCaR) {
+        table.rows[0].cells[1].colSpan = 2;
+        table.rows[0].cells[2].colSpan = 1;
+        table.rows[1].deleteCell(2);
+        table.rows[1].deleteCell(3);
+      }
+      data.query_result.data.rows.forEach(fillData.bind(null, 'tableBodyLikely'));
+
+      document.getElementById('timewindow').innerHTML = timeWindow + ' prior';
+
+      let arithmeticMeanOfLogsFX = 0;
+      let arithmeticMeanOfLogsFXOld = 0;
+      let arithmeticMeanOfLogsChrome = 0;
+      let arithmeticMeanOfLogsChromeOld = 0;
+      let arithmeticMeanOfLogsCaR = 0;
+      let numValues = 0;
+      let numValuesOld = 0;
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform != platform) {
+          return;
+        }
+        numValues++;
+        arithmeticMeanOfLogsFX += Math.log(row.FirefoxMean);
+        arithmeticMeanOfLogsChrome += Math.log(row.ChromeMean);
+        arithmeticMeanOfLogsCaR += Math.log(row.CaRMean);
+        if (row.FirefoxOldMean > 0) {
+          numValuesOld++;
+          arithmeticMeanOfLogsFXOld += Math.log(row.FirefoxOldMean);
+          arithmeticMeanOfLogsChromeOld += Math.log(row.ChromeOldMean);
+        }
+      });
+      let FxGeoMean = Math.exp(arithmeticMeanOfLogsFX / numValues);
+      let ChromeGeoMean = Math.exp(arithmeticMeanOfLogsChrome / numValues);
+      let CaRGeoMean = Math.exp(arithmeticMeanOfLogsCaR / numValues);
+      let FxOldGeoMean = Math.exp(arithmeticMeanOfLogsFXOld / numValuesOld);
+      let ChromeOldGeoMean = Math.exp(arithmeticMeanOfLogsChromeOld / numValuesOld);
+
+      let diff = FxGeoMean / ChromeGeoMean - 1;
+      let CaRdiff = FxGeoMean / CaRGeoMean - 1;
+      let oldDiff = ChromeOldGeoMean > 0 ? (FxOldGeoMean / ChromeOldGeoMean - 1) : 0;
+      let diffColor = getDiffColor(diff);
+      let oldDiffColor = getDiffColor(oldDiff);
+      let firefoxDiff = 0;
+      if (FxOldGeoMean > 0) {
+        firefoxDiff = FxGeoMean / FxOldGeoMean;
+      }
+
+      let newLine = '<tr class="sumRow"><th scope="row" class="testName">Total Score</th><td>' +
+        round(FxGeoMean, 2) + ' ms</td><td>' +
+        round(ChromeGeoMean, 2) + ' ms</td>';
+
+      if (supportsCaR) {
+        newLine += '<td>' +
+          round(CaRGeoMean, 2) + ' ms</td>';
+      }
+
+      newLine += '<td style=\'color: ' + diffColor + '\'>' +
+        round(-diff * 100, 2) + '\%</td>';
+      if (supportsCaR) {
+        newLine += '<td style=\'color: ' + getDiffColor(CaRdiff) + '\'>' +
+          round(-CaRdiff * 100, 2) + '\%</td>';
+      }
+      newLine += '<td>' +
+        round(FxOldGeoMean, 2) + ' ms</td><td>' +
+        round(ChromeOldGeoMean, 2) + ' ms</td><td style=\'color: ' + oldDiffColor + '\'>' +
+        round(-oldDiff * 100, 2) + '\%</td><td style=\'color: ' + getSelfDiffColor(firefoxDiff) + '\'>' +
+        Math.round(-(1 - firefoxDiff) * 100) + '%</td></tr >\n';
+
+      document.getElementById("tableBodyLikely").innerHTML += newLine;
+    }
+
+    function openTestComparison(signatureFirefox, signatureChrome, signatureCaR) {
+      let repoFirefox = 'mozilla-central';
+      let repoChrome = 'mozilla-central';
+      if (platform == 'android-hw-a51-11-0-aarch64-shippable-qr') {
+        repoFirefox = 'firefox-android';
+      }
+      let linkString = "https://treeherder.mozilla.org/perfherder/graphs?highlightAlerts=1&highlightChangelogData=1&highlightCommonAlerts=0&series=" + repoFirefox + "," + signatureFirefox + ",1,13&series=" + repoChrome + "," + signatureChrome + ",1,13&timerange=7776000";
+      if (supportsCaR) {
+        linkString += "&series=" + repoChrome + "," + signatureCaR + ",1,13";
+      }
+      window.open(linkString, "_blank")
+    }
+
+    function loadChart() {
+      if (searchParams.get('os') == 'osx') {
+        document.getElementById("macbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'linux') {
+        document.getElementById("linuxbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'android') {
+        document.getElementById("mobilebutton").style.backgroundColor = "gray";
+      } else if (searchParams.get('os') == 'osxm2') {
+        document.getElementById("osxm2button").style.backgroundColor = "gray";
+      } else {
+        document.getElementById("windowsbutton").style = "background-color:gray;";
+      }
+      fetch('https://sql.telemetry.mozilla.org/api/queries/97533/results.json?api_key=bf25Y5w6XgKXFcG33m3Ce9CIvdO9tT8wOros4NE0')
+        .then(function (response) {
+          return response.json();
+        })
+        .then(function (data) {
+          displayChart(data);
+        })
+        .catch(function (err) {
+          console.log('error: ' + err);
+        });
+    }
+
+    function displayChart(data) {
+      const ctx = document.getElementById('myChart').getContext('2d');
+      let xyValuesFx = [];
+      let xyValuesChrome = [];
+      let xyValuesCaR = [];
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform != platform) {
+          return;
+        }
+        // Hack to remove values where a run failed.
+        if (row.application == 'firefox' && row.geomean > 300) {
+          return;
+        }
+
+        if (row.platform == 'macosx1300-64-shippable-qr' && row.application == 'chrome' && new Date(row.push_timestamp) < new Date('2024-01-24 00:00')) {
+          return;
+        }
+
+        if (row.application == 'firefox' || row.application == 'fenix') { xyValuesFx.push({ x: row.push_timestamp, y: row.geomean }); }
+        if (row.application == 'chrome' || row.application == 'chrome-m') { xyValuesChrome.push({ x: row.push_timestamp, y: row.geomean }); }
+        if (row.application == 'custom-car' || row.application == 'cstm-car-m') { xyValuesCaR.push({ x: row.push_timestamp, y: row.geomean }); }
+      });
+
+      let cfg = {
+        type: "scatter",
+        data: {
+          datasets: [
+            {
+              label: 'Firefox',
+              pointRadius: 3,
+              pointBackgroundColor: "#FF9500",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesFx
+            }
+          ]
+        },
+        options: {
+          aspectRatio: 1.5,
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              }
+            },
+            y: {
+              beginAtZero: true
+            }
+          },
+          plugins: {
+            annotation: {
+              annotations: {
+              }
+            }
+          }
+        }
+      };
+
+      cfg.data.datasets.push({
+        label: 'Chrome',
+        pointRadius: 3,
+        pointBackgroundColor: "#1DA462",
+        pointBorderColor: "#000000",
+        pointBorderWidth: 0.5,
+        data: xyValuesChrome
+      });
+
+      if (platform == 'linux1804-64-shippable-qr') {
+        cfg.options.plugins.annotation.annotations.line3 = {
+          type: 'line',
+          xMin: '2023-02-08 17:00',
+          xMax: '2023-02-08 17:00',
+          borderColor: 'black',
+          borderWidth: 2,
+        };
+        cfg.options.plugins.annotation.annotations.label3 = {
+          type: 'label',
+          xValue: '2023-02-08 17:00',
+          yValue: 50,
+          backgroundColor: 'rgb(255,128,128)',
+          content: ['Linux Firmware Update'],
+          font: {
+            size: 14
+          },
+          rotation: -90,
+          borderRadius: 4
+        };
+      }
+
+      if (xyValuesCaR.length > 0) {
+        cfg.data.datasets.push({
+          label: 'Chromium-as-Release',
+          pointRadius: 3,
+          pointBackgroundColor: "#0099ff",
+          pointBorderColor: "#000000",
+          pointBorderWidth: 0.5,
+          data: xyValuesCaR
+        });
+      }
+
+      if (platform == 'macosx1300-64-shippable-qr') {
+        cfg.options.scales.x.min = '2023-12-01';
+      }
+
+      timeChart = new Chart("myChart", cfg);
+    }
+
+    function changeRange(newRange) {
+      if (newRange == 'all') {
+        if (platform != 'macosx1300-64-shippable-qr') {
+          timeChart.options.scales.x.min = '';
+        } else {
+          timeChart.options.scales.x.min = '2023-12-01';
+        }
+      } else {
+        var d = new Date();
+        d.setMonth(d.getMonth() - newRange);
+        timeChart.options.scales.x.min = d;
+      }
+      timeChart.update();
+    }
+
+  </script>
+  <style>
+    h3 {
+      font-family: sans-serif;
+    }
+
+    .styled-table {
+      border-collapse: collapse;
+      margin: 25px 0;
+      font-size: 0.9em;
+      font-family: sans-serif;
+      min-width: 400px;
+      box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+    }
+
+      .styled-table thead th {
+        background-color: #3366ff;
+        color: #ffffff;
+        text-align: center;
+      }
+
+      .styled-table th {
+        padding: 12px 15px;
+      }
+
+      .styled-table td {
+        text-align: right;
+      }
+
+      .styled-table .testName {
+        text-align: left;
+      }
+
+    .tableBody tr {
+      border-bottom: 1px solid #dddddd;
+    }
+
+    .tableBody .sumRow {
+      border-top: 2px solid black;
+    }
+
+    .tableBody tr:nth-of-type(odd) {
+      background-color: #f3f3f3;
+    }
+
+    .tableBody tr:nth-of-type(even) {
+      background-color: #e3e3e3;
+    }
+
+    .tableBody tr:hover {
+      background-color: #d0d0d0;
+    }
+
+    .buttons td {
+      width: 200px;
+      text-align: center;
+      font-family: sans-serif;
+      font-size: 20px;
+    }
+
+    a {
+      padding-left: 50px;
+      padding-right: 50px;
+      padding-top: 10px;
+      padding-bottom: 10px;
+      text-decoration: none;
+      color: black;
+      border-radius: 5px;
+    }
+
+      a:hover {
+        background-color: lightgray;
+      }
+  </style>
+</head>
+
+<body onload="loadChart();">
+  <center>
+    <table class="buttons">
+      <tr><td colspan="2"><a href="details-sp2.html" id="windowsbutton">Windows</a></td><td colspan="2"><a href="details-sp2.html?os=osx" id="macbutton">Mac OSX</a></td><td colspan="2"><a href="details-sp2.html?os=linux" id="linuxbutton">Linux</a></td></tr>
+      <tr><td colspan="3" style="height:80px;"><a href="details-sp2.html?os=android" id="mobilebutton">Android</a></td><td colspan="3" style="height:80px;"><a href="details-sp2.html?os=osxm2" id="osxm2button">Mac OSX (M2)</a></td></tr>
+    </table>
+
+    <h3>Current Status (lower is better)</h3>
+
+    <div style="width:100%;max-width:900px">
+      <canvas id="myChart" style="width:100%;max-width:900px"></canvas>
+      <table class="buttons">
+        <tr><td><a onclick="changeRange('all')" id="allbutton">All time</a></td><td><a onclick="changeRange(3)" id="month3button">3 months</a></td><td><a onclick="changeRange(1)" id="month1button">1 month</a></td></tr>
+      </table>
+    </div>
+
+    <h3>Breakdown: Speedometer 3 Likely Candidates</h3>
+    <table class="styled-table" id="tableLikely">
+      <col>
+      <col>
+      <colgroup span="2"></colgroup>
+      <col>
+      <colgroup span="2"></colgroup>
+      <col>
+      <thead>
+        <tr><th rowspan="2"></th><th colspan="3" scope="colgroup">Total time (Lower is better)<br />1wk moving average</th><th colspan="2">Difference</th><th colspan="2" scope="colgroup" id="timewindow">6wk prior</th><th rowspan="2">Difference</th><th rowspan="2">Firefox<br />Evolution</th></tr>
+        <tr><th scope="col">Firefox<br />Nightly</th><th scope="col">Chrome<br />Release</th><th scope="col">Chromium-<br />As-Release</th><th scope="col">Chrome<br />Release</th><th scope="col">Chromium-<br />As-Release</th><th scope="col">Firefox<br />Nightly</th><th scope="col">Chrome<br />Release</th></tr>
+      </thead>
+      <tbody class="tableBody" id="tableBodyLikely">
+      </tbody>
+    </table>
+  </center>
+</body>
+</html>
diff --git a/details-subtests.html b/details-subtests.html
new file mode 100644 (file)
index 0000000..00ac55d
--- /dev/null
@@ -0,0 +1,297 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Speedometer 3 Status Dashboard</title>
+
+  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
+
+  <script>
+    function round(number, decimals) {
+      return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
+    }
+
+    var platform = 'windows10-64-shippable-qr';
+
+    const searchParams = new URLSearchParams(window.location.search);
+
+    let supportsCaR = true;
+
+    let dataSetVisible = [true, true, true];
+    let charts = [];
+
+    if (searchParams.get('os') == 'osx') {
+      platform = 'macosx1015-64-shippable-qr';
+    } else if (searchParams.get('os') == 'osxm2') {
+      platform = 'macosx1400-64-shippable-qr';
+    } else if (searchParams.get('os') == 'linux') {
+      platform = 'linux1804-64-shippable-qr';
+    } else if (searchParams.get('os') == 'android') {
+      platform = 'android-hw-a51-11-0-aarch64-shippable-qr';
+    }
+
+    function getDiffColor(diff) {
+      if (diff < -0.05) {
+        return 'green'
+      } else if (diff < 0.095) {
+        return 'black'
+      } else if (diff < 0.2) {
+        return '#b38f00'
+      } else {
+        return 'red'
+      }
+    }
+
+    function getSelfDiffColor(diff) {
+      if (diff <= 0.98) {
+        return 'green';
+      } else if (diff >= 1.02) {
+        return 'red';
+      } else {
+        return 'black';
+      }
+    }
+
+    function updateVisibility() {
+      if (dataSetVisible[0]) {
+        document.getElementById("fxbutton").style = "background-color:gray;";
+      } else {
+        document.getElementById("fxbutton").style = "";
+      }
+      if (dataSetVisible[1]) {
+        document.getElementById("chromebutton").style = "background-color:gray;";
+      } else {
+        document.getElementById("chromebutton").style = "";
+      }
+      if (dataSetVisible[2]) {
+        document.getElementById("carbutton").style = "background-color:gray;";
+      } else {
+        document.getElementById("carbutton").style = "";
+      }
+
+      charts.forEach((chart) => {
+        let i = 0;
+        chart.data.datasets.forEach((dataset) => {
+          dataset.hidden = !dataSetVisible[i++];
+        });
+        chart.update();
+      });
+    }
+
+    function loadCharts() {
+      if (searchParams.get('os') == 'osx') {
+        document.getElementById("macbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'osxm2') {
+        document.getElementById("osxm2button").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'linux') {
+        document.getElementById("linuxbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'android') {
+        document.getElementById("mobilebutton").style.backgroundColor = "gray";
+      } else {
+        document.getElementById("windowsbutton").style = "background-color:gray;";
+      }
+      fetch('https://sql.telemetry.mozilla.org/api/queries/93533/results.json?api_key=snFMJTpWr0lnSy2AHg1sAO68BADqDgWC5Cn9Eg6b')
+        .then(function (response) {
+          return response.json();
+        })
+        .then(function (data) {
+          displayCharts(data);
+        })
+        .catch(function (err) {
+          console.log('error: ' + err);
+        });
+      updateVisibility();
+    }
+
+    function displayCharts(data) {
+      let xyValuesFx = {};
+      let xyValuesChrome = {};
+      let xyValuesCaR = {};
+      let xyValuesSafari = {};
+      let signatures = {};
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform != platform) {
+          return;
+        }
+
+        if (typeof xyValuesFx[row.test] == "undefined") {
+          xyValuesFx[row.test] = [];
+          xyValuesChrome[row.test] = [];
+          xyValuesCaR[row.test] = [];
+          xyValuesSafari[row.test] = [];
+          signatures[row.test] = {};
+        }
+
+        signatures[row.test][row.application] = row.signature_id;
+
+        if (row.application == 'firefox' || row.application == 'fenix') { xyValuesFx[row.test].push({ x: row.push_timestamp, y: row.mean }); }
+        if (row.application == 'chrome' || row.application == 'chrome-m') { xyValuesChrome[row.test].push({ x: row.push_timestamp, y: row.mean }); }
+        if (row.application == 'custom-car' || row.application == 'cstm-car-m') { xyValuesCaR[row.test].push({ x: row.push_timestamp, y: row.mean }); }
+        if (row.application == 'safari') { xyValuesSafari[row.test].push({ x: row.push_timestamp, y: row.mean }); }
+      });
+
+      let i = 0;
+      let cfg = {};
+      for (var test in xyValuesFx) {
+        let chartHolder = document.getElementById("chart-holder");
+
+        graphLink = "https://treeherder.mozilla.org/perfherder/graphs?highlightAlerts=1&highlightChangelogData=1&highlightCommonAlerts=0";
+
+        for (var app in signatures[test]) {
+          graphLink += "&series=mozilla-central," + signatures[test][app] + ",1,13"
+        }
+
+        chartHolder.innerHTML += '<center><h3>' + test + ' <a style="padding-left: 10px; padding-right: 10px; " href="' + graphLink + '">&#128279;</a></h3></center><canvas id="myChart-' + test + '" style="width: 100%; max-width: 900px"></canvas>';
+      }
+
+
+      for (var test in xyValuesFx) {
+        let cfg = {
+          type: "scatter",
+          data: {
+            datasets: [
+              {
+                label: 'Firefox',
+                pointRadius: 4,
+                pointBackgroundColor: "#FF9500",
+                data: xyValuesFx[test]
+              },
+              {
+                label: 'Chrome',
+                pointRadius: 4,
+                pointBackgroundColor: "#1DA462",
+                data: xyValuesChrome[test]
+              }
+            ]
+          },
+          options: {
+            aspectRatio: 1.5,
+            scales: {
+              x: {
+                type: 'time',
+                time: {
+                  unit: 'day'
+                }
+              },
+              y: {
+                beginAtZero: true
+              }
+            },
+          }
+        };
+
+        if (xyValuesCaR[test].length > 0) {
+          cfg.data.datasets.push({
+            label: 'Chromium-as-Release',
+            pointRadius: 4,
+            pointBackgroundColor: "#0099ff",
+            data: xyValuesCaR[test]
+          });
+        }
+
+        if (xyValuesSafari[test].length > 0) {
+          cfg.data.datasets.push({
+            label: 'Safari',
+            pointRadius: 4,
+            pointBackgroundColor: "#44444444",
+            pointBorderColor: "#000000",
+            data: xyValuesSafari[test]
+          });
+        }
+
+        charts.push(new Chart("myChart-" + test, cfg));
+      }
+    }
+
+  </script>
+  <style>
+    h3 {
+      font-family: sans-serif;
+    }
+
+    .styled-table {
+      border-collapse: collapse;
+      margin: 25px 0;
+      font-size: 0.9em;
+      font-family: sans-serif;
+      min-width: 400px;
+      box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+    }
+
+      .styled-table thead th {
+        background-color: #3366ff;
+        color: #ffffff;
+        text-align: center;
+      }
+
+      .styled-table th {
+        padding: 12px 15px;
+      }
+
+      .styled-table td {
+        text-align: right;
+      }
+
+      .styled-table .testName {
+        text-align: right;
+      }
+
+    .tableBody tr {
+      border-bottom: 1px solid #dddddd;
+    }
+
+    .tableBody .sumRow {
+      border-top: 2px solid black;
+    }
+
+    .tableBody tr:nth-of-type(odd) {
+      background-color: #f3f3f3;
+    }
+
+    .tableBody tr:nth-of-type(even) {
+      background-color: #e3e3e3;
+    }
+
+    .tableBody tr:hover {
+      background-color: #d0d0d0;
+    }
+
+    .buttons td {
+      width: 200px;
+      text-align: center;
+      font-family: sans-serif;
+      font-size: 20px;
+    }
+
+    a {
+      padding-left: 50px;
+      padding-right: 50px;
+      padding-top: 10px;
+      padding-bottom: 10px;
+      text-decoration: none;
+      color: black;
+      border-radius: 5px;
+    }
+
+      a:hover {
+        background-color: lightgray;
+      }
+  </style>
+</head>
+
+<body onload="loadCharts();">
+  <center>
+    <table class="buttons">
+      <tr><td colspan="2"><a href="details-subtests.html" id="windowsbutton">Windows</a></td><td colspan="2"><a href="details-subtests.html?os=osx" id="macbutton">Mac OSX</a></td><td colspan="2"><a href="details-subtests.html?os=linux" id="linuxbutton">Linux</a></td></tr>
+      <tr><td colspan="3" style="height:80px;"><a href="details-subtests.html?os=android" id="mobilebutton">Android</a></td><td colspan="3" style="height:80px;"><a href="details-subtests.html?os=osxm2" id="osxm2button">Mac OSX (M2)</a></td></tr>
+      <tr><td colspan="2"><a id="fxbutton" href="javascript:dataSetVisible[0] = !dataSetVisible[0]; updateVisibility();">Firefox</a></td><td colspan="2"><a id="chromebutton" href="javascript:dataSetVisible[1] = !dataSetVisible[1]; updateVisibility();">Chrome</a></td><td colspan="2"><a id="carbutton" href="javascript:dataSetVisible[2] = !dataSetVisible[2]; updateVisibility();">CaR</a></td></tr>
+    </table>
+
+    <div style="width:100%;max-width:900px" id="chart-holder">
+      
+    </div>
+  </center>
+</body>
+</html>
diff --git a/details.html b/details.html
new file mode 100644 (file)
index 0000000..b874abf
--- /dev/null
@@ -0,0 +1,622 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Speedometer 3 Status Dashboard</title>
+
+  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
+
+  <script>
+    function round(number, decimals) {
+      return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
+    }
+
+    var platform = 'windows10-64-shippable-qr';
+
+    const searchParams = new URLSearchParams(window.location.search);
+
+    let likelyDataURL = 'https://sql.telemetry.mozilla.org/api/queries/89943/results.json?api_key=8pVTrtmbttRWqE5MGuCplgHncRFHON0JUj5zCSGh';
+    let timeWindow = '6wk';
+    let supportsCaR = true;
+
+    let range = '';
+
+    var timeChart;
+
+    var total_sig_firefox = 4591185;
+    var total_sig_chrome = 4592500;
+    var total_sig_car = 4781109;
+    var total_sig_safari = 0;
+    var total_sig_safari_tp = 0;
+    if (searchParams.get('os') == 'osx') {
+      total_sig_firefox = 4588949;
+      total_sig_chrome = 4589463;
+      total_sig_car = 4836588;
+      platform = 'macosx1015-64-shippable-qr';
+    } else if (searchParams.get('os') == 'osxm2') {
+      total_sig_firefox = 5042783;
+      total_sig_chrome = 5044184;
+      total_sig_car = 5043640;
+      total_sig_safari = 5045302;
+      total_sig_safari_tp = 37211;
+      platform = 'macosx1400-64-shippable-qr';
+    } else if (searchParams.get('os') == 'linux') {
+      total_sig_firefox = 4588786;
+      total_sig_chrome = 4589789;
+      total_sig_car = 4725642;
+      platform = 'linux1804-64-shippable-qr';
+    } else if (searchParams.get('os') == 'android') {
+      total_sig_firefox = 4654149;
+      total_sig_chrome = 4698114;
+      total_sig_car = 4942846;
+      platform = 'android-hw-a51-11-0-aarch64-shippable-qr';
+      likelyDataURL = 'https://sql.telemetry.mozilla.org/api/queries/90959/results.json?api_key=uuoiM8Xdob1HcGB5UxdFG3TvZi6D08kfNR5pWC9O';
+      timeWindow = '4wk';
+    } else if (searchParams.get('os') == 'android-s21') {
+      total_sig_firefox = 5052258;
+      total_sig_chrome = 5051172;
+      total_sig_car = 5052905;
+      platform = 'android-hw-s21-13-0-android-aarch64-shippable-qr';
+      likelyDataURL = 'https://sql.telemetry.mozilla.org/api/queries/90959/results.json?api_key=uuoiM8Xdob1HcGB5UxdFG3TvZi6D08kfNR5pWC9O';
+      timeWindow = '4wk';
+    } else if (searchParams.get('os') == 'android-p6') {
+      total_sig_firefox = 5051862;
+      total_sig_chrome = 5050973;
+      total_sig_car = 5053103;
+      platform = 'android-hw-p6-13-0-android-aarch64-shippable-qr';
+      likelyDataURL = 'https://sql.telemetry.mozilla.org/api/queries/90959/results.json?api_key=uuoiM8Xdob1HcGB5UxdFG3TvZi6D08kfNR5pWC9O';
+      timeWindow = '4wk';
+    }
+
+    fetch(likelyDataURL)
+      .then(function (response) {
+        return response.json();
+      })
+      .then(function (data) {
+        loadDataLikely(data);
+      })
+      .catch(function (err) {
+        console.log('error: ' + err);
+      });
+
+    function getDiffColor(diff) {
+      if (diff < -0.05) {
+        return 'green'
+      } else if (diff < 0.095) {
+        return 'black'
+      } else if (diff < 0.2) {
+        return '#b38f00'
+      } else {
+        return 'red'
+      }
+    }
+
+    function getSelfDiffColor(diff) {
+      if (diff <= 0.98) {
+        return 'green';
+      } else if (diff >= 1.02) {
+        return 'red';
+      } else {
+        return 'black';
+      }
+    }
+
+    function fillData(tableId, row) {
+      if (row.platform != platform) {
+        return;
+      }
+      diffColor = getDiffColor(row.difference);
+      oldDiffColor = getDiffColor(row.olddifference);
+
+      let firefoxDiff = 1.0;
+      if (row.firefoxoldmean > 0) {
+        firefoxDiff = row.firefoxdifference;
+      }
+
+      
+      let newLine = '';
+      newLine += '<tr onClick="openTestComparison(' + row.signature_id_firefox + ', ' + row.signature_id_chrome + ', ' + row.signature_id_car + ', ' + row.signature_id_safari + ', ' + row.signature_id_safari_tp + ');"><th scope="row" class="testName">' +
+        row.test.split('/')[0] + '</th><td>' +
+        round(row.firefoxmean, 2) + ' ms</td><td>' +
+        round(row.chromemean, 2) + ' ms</td>';
+      if (supportsCaR) {
+        newLine += '<td>' + round(row.carmean, 2) + ' ms</td>';
+      }
+
+      if (platform == 'macosx1400-64-shippable-qr') {
+        newLine += '<td>' +
+          round(row.safarimean, 2) + ' ms</td>';
+        newLine += '<td>' +
+          round(row.safaritpmean, 2) + ' ms</td>';
+      }
+
+      newLine += '<td style =\'color: ' + diffColor + '\'>' +
+        Math.round(-row.difference * 100) + '\%</td>'
+      if (supportsCaR) {
+        newLine += '<td style=\'color: ' + getDiffColor(row.cardifference) + '\'>' + Math.round(-row.cardifference * 100) + '\%</td>'
+      }
+
+      if (platform == 'macosx1400-64-shippable-qr') {
+        newLine += '<td style=\'color: ' + getDiffColor(row.safaridifference) + '\'>' + Math.round(-row.safaridifference * 100) + '\%</td>'
+        newLine += '<td style=\'color: ' + getDiffColor(row.safaritpdifference) + '\'>' + Math.round(-row.safaritpdifference * 100) + '\%</td>'
+      }
+      newLine += '<td>' +
+        round(row.firefoxoldmean, 2) + ' ms</td><td>' +
+        round(row.chromeoldmean, 2) + ' ms</td><td style=\'color: ' + oldDiffColor + '\'>' +
+        Math.round(-row.olddifference * 100) + '\%</td><td style=\'color: ' + getSelfDiffColor(firefoxDiff) + '\'>' +
+        Math.round(-(1 - firefoxDiff) * 100) + '%</td></tr >\n';
+
+      document.getElementById(tableId).innerHTML += newLine;
+    }
+
+    function loadDataLikely(data) {
+      let table = document.getElementById('tableLikely');
+      if (platform != 'macosx1400-64-shippable-qr') {
+        table.rows[0].cells[1].colSpan = 3;
+        table.rows[0].cells[2].colSpan = 2;
+        table.rows[1].deleteCell(3);
+        table.rows[1].deleteCell(3);
+        table.rows[1].deleteCell(5);
+        table.rows[1].deleteCell(5);
+      }
+      data.query_result.data.rows.forEach(fillData.bind(null, 'tableBodyLikely'));
+
+      document.getElementById('timewindow').innerHTML = timeWindow + ' prior';
+
+      let arithmeticMeanOfLogsFX = 0;
+      let arithmeticMeanOfLogsFXOld = 0;
+      let arithmeticMeanOfLogsChrome = 0;
+      let arithmeticMeanOfLogsChromeOld = 0;
+      let arithmeticMeanOfLogsCaR = 0;
+      let arithmeticMeanOfLogsSafari = 0;
+      let arithmeticMeanOfLogsSafariTP = 0;
+      let numValues = 0;
+      let numValuesOld = 0;
+      data.query_result.data.rows.forEach(function (row) {
+        if ((row.platform != platform)) {
+          return;
+        }
+        numValues++;
+        arithmeticMeanOfLogsFX += Math.log(row.firefoxmean);
+        arithmeticMeanOfLogsChrome += Math.log(row.chromemean);
+        arithmeticMeanOfLogsCaR += Math.log(row.carmean);
+        arithmeticMeanOfLogsSafari += Math.log(row.safarimean);
+        arithmeticMeanOfLogsSafariTP += Math.log(row.safaritpmean);
+        if (row.firefoxoldmean > 0) {
+          numValuesOld++;
+          arithmeticMeanOfLogsFXOld += Math.log(row.firefoxoldmean);
+          arithmeticMeanOfLogsChromeOld += Math.log(row.chromeoldmean);
+        }
+      });
+      let FxGeoMean = Math.exp(arithmeticMeanOfLogsFX / numValues);
+      let ChromeGeoMean = Math.exp(arithmeticMeanOfLogsChrome / numValues);
+      let CaRGeoMean = Math.exp(arithmeticMeanOfLogsCaR / numValues);
+      let SafariGeoMean = Math.exp(arithmeticMeanOfLogsSafari / numValues);
+      let SafariTPGeoMean = Math.exp(arithmeticMeanOfLogsSafariTP / numValues);
+      let FxOldGeoMean = Math.exp(arithmeticMeanOfLogsFXOld / numValuesOld);
+      let ChromeOldGeoMean = Math.exp(arithmeticMeanOfLogsChromeOld / numValuesOld);
+
+      let diff = FxGeoMean / ChromeGeoMean - 1;
+      let CaRdiff = FxGeoMean / CaRGeoMean - 1;
+      let Safaridiff = FxGeoMean / SafariGeoMean - 1;
+      let SafariTPdiff = FxGeoMean / SafariTPGeoMean - 1;
+      let oldDiff = ChromeOldGeoMean > 0 ? (FxOldGeoMean / ChromeOldGeoMean - 1) : 0;
+      let diffColor = getDiffColor(diff);
+      let oldDiffColor = getDiffColor(oldDiff);
+      let firefoxDiff = 0;
+      if (FxOldGeoMean > 0) {
+        firefoxDiff = FxGeoMean / FxOldGeoMean;
+      }
+
+      let newLine = '<tr onClick="openTestComparison(' + total_sig_firefox + ', ' + total_sig_chrome + ', ' + total_sig_car + ', ' + total_sig_safari + ', ' + total_sig_safari_tp + ');" class="sumRow"><th scope="row" class="testName">Total Score</th><td>' +
+        round(FxGeoMean, 2) + ' ms</td><td>' +
+        round(ChromeGeoMean, 2) + ' ms</td>';
+
+      if (supportsCaR) {
+        newLine += '<td>' +
+          round(CaRGeoMean, 2) + ' ms</td>';
+      }
+
+      if (platform == 'macosx1400-64-shippable-qr') {
+        newLine += '<td>' +
+          round(SafariGeoMean, 2) + ' ms</td>';
+        newLine += '<td>' +
+          round(SafariTPGeoMean, 2) + ' ms</td>';
+      }
+
+      newLine += '<td style=\'color: ' + diffColor + '\'>' +
+        round(-diff * 100, 2) + '\%</td>';
+      if (supportsCaR) {
+        newLine += '<td style=\'color: ' + getDiffColor(CaRdiff) + '\'>' +
+          round(-CaRdiff * 100, 2) + '\%</td>';
+      }
+
+      if (platform == 'macosx1400-64-shippable-qr') {
+        newLine += '<td style=\'color: ' + getDiffColor(Safaridiff) + '\'>' +
+          round(-Safaridiff * 100, 2) + '\%</td>';
+        newLine += '<td style=\'color: ' + getDiffColor(SafariTPdiff) + '\'>' +
+          round(-SafariTPdiff * 100, 2) + '\%</td>';
+      }
+
+      newLine += '<td>' +
+        round(FxOldGeoMean, 2) + ' ms</td><td>' +
+        round(ChromeOldGeoMean, 2) + ' ms</td><td style=\'color: ' + oldDiffColor + '\'>' +
+        round(-oldDiff * 100, 2) + '\%</td><td style=\'color: ' + getSelfDiffColor(firefoxDiff) + '\'>' +
+        Math.round(-(1 - firefoxDiff) * 100) + '%</td></tr >\n';
+
+      document.getElementById("tableBodyLikely").innerHTML += newLine;
+    }
+
+    function openTestComparison(signatureFirefox, signatureChrome, signatureCaR, signatureSafari, signatureSafariTP) {
+      let repoFirefox = 'mozilla-central';
+      let repoChrome = 'mozilla-central';
+
+      let linkString = "https://treeherder.mozilla.org/perfherder/graphs?highlightAlerts=1&highlightChangelogData=1&highlightCommonAlerts=0&series=" + repoFirefox + "," + signatureFirefox + ",1,13&series=" + repoChrome + "," + signatureChrome + ",1,13&timerange=7776000";
+      if (supportsCaR) {
+        linkString += "&series=" + repoChrome + "," + signatureCaR + ",1,13";
+      }
+      if (signatureSafari > 0) {
+        linkString += "&series=" + repoChrome + "," + signatureSafari + ",1,13";
+      }
+      if (signatureSafariTP > 0) {
+        linkString += "&series=" + repoChrome + "," + signatureSafariTP + ",1,13";
+      }
+      window.open(linkString, "_blank")
+    }
+
+    function loadChart() {
+      if (searchParams.get('os') == 'osx') {
+        document.getElementById("macbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'linux') {
+        document.getElementById("linuxbutton").style = "background-color:gray;";
+      } else if (searchParams.get('os') == 'android') {
+        document.getElementById("mobilebutton").style.backgroundColor = "gray";
+      } else if (searchParams.get('os') == 'android-s21') {
+        document.getElementById("mobilebutton2").style.backgroundColor = "gray";
+      } else if (searchParams.get('os') == 'android-p6') {
+        document.getElementById("mobilebutton3").style.backgroundColor = "gray";
+      } else if (searchParams.get('os') == 'osxm2') {
+        document.getElementById("osxm2button").style.backgroundColor = "gray";
+      } else {
+        document.getElementById("windowsbutton").style = "background-color:gray;";
+      }
+      fetch('https://sql.telemetry.mozilla.org/api/queries/89971/results.json?api_key=6cd9JzffuVjIytWO8fuXs33HXKRjiy9bHJmHfndj')
+        .then(function (response) {
+          return response.json();
+        })
+        .then(function (data) {
+          displayChart(data);
+        })
+        .catch(function (err) {
+          console.log('error: ' + err);
+        });
+    }
+
+    function displayChart(data) {
+      const ctx = document.getElementById('myChart').getContext('2d');
+      let xyValuesFx = [];
+      let xyValuesChrome = [];
+      let xyValuesCaR = [];
+      let xyValuesSafari = [];
+      let xyValuesSafariTP = [];
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform != platform) {
+          if (!(row.platform == 'macosx1300-64-shippable-qr' && platform == 'macosx1400-64-shippable-qr')) {
+            return;
+          }
+        }
+
+        if (row.geomean > 130 && !(platform == 'android-hw-a51-11-0-aarch64-shippable-qr' || platform == 'android-hw-s21-13-0-android-aarch64-shippable-qr' || platform == 'android-hw-p6-13-0-android-aarch64-shippable-qr')) {
+          return;
+        }
+
+        if (row.platform == 'macosx1300-64-shippable-qr' && row.application == 'chrome' && new Date(row.push_timestamp) < new Date('2024-01-24 00:00')) {
+          return;
+        }
+
+        if (row.application == 'firefox' || row.application == 'fenix') { xyValuesFx.push({ x: row.push_timestamp, y: row.geomean }); }
+        if (row.application == 'chrome' || row.application == 'chrome-m') { xyValuesChrome.push({ x: row.push_timestamp, y: row.geomean }); }
+        if (row.application == 'custom-car' || row.application == 'cstm-car-m') { xyValuesCaR.push({ x: row.push_timestamp, y: row.geomean }); }
+        if (row.application == 'safari') { xyValuesSafari.push({ x: row.push_timestamp, y: row.geomean }); }
+        if (row.application == 'safari-tp') { xyValuesSafariTP.push({ x: row.push_timestamp, y: row.geomean }); }
+      });
+
+      let cfg = {
+        type: "scatter",
+        data: {
+          datasets: [
+            {
+              label: 'Firefox',
+              pointRadius: 3,
+              pointBackgroundColor: "#FF9500",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesFx
+            }
+          ]
+        },
+        options: {
+          aspectRatio: 1.5,
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              }
+            },
+            y: {
+              beginAtZero: true
+            }
+          },
+          plugins: {
+            annotation: {
+              annotations: {
+                line5: {
+                  type: 'line',
+                  xMin: '2023-06-07 00:00',
+                  xMax: '2023-06-07 00:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label5: {
+                  type: 'label',
+                  xValue: '2023-06-07 00:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 200 : 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Multiple Tests Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line6: {
+                  type: 'line',
+                  xMin: '2023-06-15 08:00',
+                  xMax: '2023-06-15 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label6: {
+                  type: 'label',
+                  xValue: '2023-06-15 08:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 200 : 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Perf-Dashboard Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line7: {
+                  type: 'line',
+                  xMin: '2023-07-04 21:00',
+                  xMax: '2023-07-04 21:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label7: {
+                  type: 'label',
+                  xValue: '2023-07-04 21:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 200 : 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['rAF Switch/Tests Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line8: {
+                  type: 'line',
+                  xMin: '2023-12-30 17:00',
+                  xMax: '2023-12-30 17:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label8: {
+                  type: 'label',
+                  xValue: '2023-12-30 17:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 200 : 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Complex-DOM Workloads/Freeze'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line9: {
+                  type: 'line',
+                  xMin: '2024-03-09 00:00',
+                  xMax: '2024-03-09 00:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label9: {
+                  type: 'label',
+                  xValue: '2024-03-09 00:00',
+                  yValue: platform == 'android-hw-a51-11-0-aarch64-shippable-qr' ? 200 : 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Release'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+              }
+            }
+          }
+        }
+      };
+
+      if (platform == 'android-hw-s21-13-0-android-aarch64-shippable-qr' || platform == 'android-hw-p6-13-0-android-aarch64-shippable-qr') {
+        cfg.options.plugins = {};
+      }
+
+      cfg.data.datasets.push({
+        label: 'Chrome',
+        pointRadius: 3,
+        pointBackgroundColor: "#1DA462",
+        pointBorderColor: "#000000",
+        pointBorderWidth: 0.5,
+        data: xyValuesChrome
+      });
+
+      if (xyValuesCaR.length > 0) {
+        cfg.data.datasets.push({
+          label: 'Chromium-as-Release',
+          pointRadius: 3,
+          pointBackgroundColor: "#2773da",
+          pointBorderColor: "#000000",
+          pointBorderWidth: 0.5,
+          data: xyValuesCaR
+        });
+      }
+
+      if (platform == 'macosx1400-64-shippable-qr') {
+        cfg.data.datasets.push({
+          label: 'Safari',
+          pointRadius: 3,
+          pointBackgroundColor: "#44444444",
+          pointBorderColor: "#000000",
+          pointBorderWidth: 0.5,
+          data: xyValuesSafari
+        });
+        cfg.options.scales.x.min = '2023-12-01';
+      }
+
+      timeChart = new Chart("myChart", cfg);
+    }
+
+    function changeRange(newRange) {
+      if (newRange == 'all') {
+        if (platform != 'macosx1400-64-shippable-qr') {
+          timeChart.options.scales.x.min = '';
+        } else {
+          timeChart.options.scales.x.min = '2023-12-01';
+        }
+      } else {
+        var d = new Date();
+        d.setMonth(d.getMonth() - newRange);
+        timeChart.options.scales.x.min = d;
+      }
+      timeChart.update();
+    }
+
+  </script>
+  <style>
+    h3 {
+      font-family: sans-serif;
+    }
+
+    .styled-table {
+      border-collapse: collapse;
+      margin: 25px 0;
+      font-size: 0.9em;
+      font-family: sans-serif;
+      min-width: 400px;
+      box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+    }
+
+      .styled-table thead th {
+        background-color: #3366ff;
+        color: #ffffff;
+        text-align: center;
+      }
+
+      .styled-table th {
+        padding: 12px 15px;
+      }
+
+      .styled-table td {
+        text-align: right;
+      }
+
+      .styled-table .testName {
+        text-align: left;
+      }
+
+    .tableBody tr {
+      border-bottom: 1px solid #dddddd;
+    }
+
+    .tableBody .sumRow {
+      border-top: 2px solid black;
+    }
+
+    .tableBody tr:nth-of-type(odd) {
+      background-color: #f3f3f3;
+    }
+
+    .tableBody tr:nth-of-type(even) {
+      background-color: #e3e3e3;
+    }
+
+    .tableBody tr:hover {
+      background-color: #d0d0d0;
+    }
+
+    .buttons td {
+      width: 250px;
+      text-align: center;
+      font-family: sans-serif;
+      font-size: 20px;
+    }
+
+    a {
+      padding-left: 30px;
+      padding-right: 30px;
+      padding-top: 10px;
+      padding-bottom: 10px;
+      text-decoration: none;
+      color: black;
+      border-radius: 5px;
+    }
+
+      a:hover {
+        background-color: lightgray;
+      }
+  </style>
+</head>
+
+<body onload="loadChart();">
+  <center>
+    <table class="buttons">
+      <tr><td colspan="6" style="height:80px;"><a href="index.html">Overall</a></td></tr>
+      <tr><td colspan="3"><a href="details.html" id="windowsbutton">Windows</a></td><td colspan="3"><a href="details.html?os=linux" id="linuxbutton">Linux</a></td></tr>
+      <tr><td colspan="3"><a href="details.html?os=osx" id="macbutton">Mac OSX</a></td><td colspan="3" style="height:50px;"><a href="details.html?os=osxm2" id="osxm2button">Mac OSX (M2)</a></td></tr>
+      <tr><td colspan="2" style="height:50px;"><a href="details.html?os=android" id="mobilebutton">Android (A51)</a></td><td colspan="2" style="height:50px;"><a href="details.html?os=android-s21" id="mobilebutton2">Android (S21)</a></td><td colspan="2" style="height:50px;"><a href="details.html?os=android-p6" id="mobilebutton3">Android (Pixel 6)</a></td></tr>
+    </table>
+
+    <h3>Current Status (lower is better)</h3>
+
+    <div style="width:100%;max-width:900px">
+      <canvas id="myChart" style="width:100%;max-width:900px"></canvas>
+      <table class="buttons">
+        <tr><td><a onclick="changeRange('all')" id="allbutton">All time</a></td><td><a onclick="changeRange(3)" id="month3button">3 months</a></td><td><a onclick="changeRange(1)" id="month1button">1 month</a></td></tr>
+      </table>
+    </div>
+
+    <h3>Breakdown: Speedometer 3 Likely Candidates</h3>
+    <table class="styled-table" id="tableLikely">
+      <col>
+      <col>
+      <colgroup span="2"></colgroup>
+      <col>
+      <colgroup span="2"></colgroup>
+      <col>
+      <thead>
+        <tr><th rowspan="2"></th><th colspan="5" scope="colgroup">Total time (Lower is better)<br />1wk moving average</th><th colspan="4">Difference</th><th colspan="2" scope="colgroup" id="timewindow">6wk prior</th><th rowspan="2">Difference</th><th rowspan="2">Firefox<br />Evolution</th></tr>
+        <tr><th scope="col">Firefox<br />Nightly</th><th scope="col">Chrome<br />Release</th><th scope="col">Chromium-<br />As-Release</th><th scope="col">Safari<br/>Release</th><th scope="col">Safari<br />TP</th><th scope="col">Chrome<br />Release</th><th scope="col">Chromium-<br />As-Release</th><th scope="col">Safari<br/>Release</th><th scope="col">Safari<br />TP</th><th scope="col">Firefox<br />Nightly</th><th scope="col">Chrome<br />Release</th></tr>
+      </thead>
+      <tbody class="tableBody" id="tableBodyLikely">
+      </tbody>
+    </table>
+  </center>
+</body>
+</html>
diff --git a/diff-overlay.html b/diff-overlay.html
new file mode 100644 (file)
index 0000000..7fe71c3
--- /dev/null
@@ -0,0 +1,163 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Speedometer 3 Status Dashboard</title>
+
+  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
+
+  <script>
+    function round(number, decimals) {
+      return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
+    }
+
+    function getDiffColor(diff) {
+      if (diff < -0.05) {
+        return 'green'
+      } else if (diff < 0.095) {
+        return 'black'
+      } else if (diff < 0.2) {
+        return '#b38f00'
+      } else {
+        return 'red'
+      }
+    }
+
+    function loadChart() {
+      fetch('https://sql.telemetry.mozilla.org/api/queries/91105/results.json?api_key=l1VW7NobZMxWLDNpLHMYMMcLsBKFSeeoWBZi0gpZ')
+        .then(function (response) {
+          return response.json();
+        })
+        .then(function (data) {
+          displayChart(data);
+        })
+        .catch(function (err) {
+          console.log('error: ' + err);
+        });
+    }
+
+    function getPlatformWeight(platform) {
+      switch (platform) {
+        case 'windows10-64-shippable-qr':
+          return 0.8;
+        case 'macosx1015-64-shippable-qr':
+          return 0.2;
+        default:
+          return 0.0;
+      }
+    }
+
+    function displayChart(data) {
+      const ctxDesktop = document.getElementById('chartDesktop').getContext('2d');
+
+      let xyValues = {};
+
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.difference == null) {
+          return;
+        }
+
+        if (!xyValues.hasOwnProperty(row.platform)) {
+          xyValues[row.platform] = [];
+        }
+
+        xyValues[row.platform].push({ x: row.push_timestamp, y: -row.moving_difference * 100 });
+      });
+
+
+      new Chart("chartDesktop", {
+        type: "line",
+        data: {
+          datasets: [
+            {
+              label: 'Windows',
+              data: xyValues['windows10-64-shippable-qr']
+            },
+            {
+              label: 'Mac OS',
+              data: xyValues['macosx1015-64-shippable-qr']
+            },
+            {
+              label: 'Linux',
+              data: xyValues['linux1804-64-shippable-qr']
+            },
+            {
+              label: 'Android',
+              data: xyValues['android-hw-a51-11-0-aarch64-shippable-qr']
+            }
+          ]
+        },
+        options: {
+          aspectRatio: 2,
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              }
+            },
+            y: {
+              beginAtZero: true,
+              title: {
+                display: true,
+                text: 'Performance Difference (%)'
+              },
+            }
+          }
+        }
+      });
+    }
+
+  </script>
+  <style>
+    h2 {
+      font-family: sans-serif;
+    }
+
+    th {
+      font-family: sans-serif;
+      font-size: 22px;
+      text-align: center;
+      width: 180px;
+    }
+
+    td {
+      font-family: sans-serif;
+      font-size: 20px;
+      text-align: center;
+    }
+
+    .buttons td {
+      width: 200px;
+    }
+
+    a {
+      padding-left: 50px;
+      padding-right: 50px;
+      padding-top: 10px;
+      padding-bottom: 10px;
+      text-decoration: none;
+      color: black;
+      border-radius: 5px;
+    }
+
+      a:hover {
+        background-color: lightgray;
+      }
+  </style>
+</head>
+
+<body onload="loadChart();">
+  <center>
+
+    <h2>Speedometer 3 Comparison to Chrome</h2>
+
+    <div style="width:100%;max-width:900px">
+      <canvas id="chartDesktop" style="width:100%;max-width:900px"></canvas>
+    </div>
+
+  </center>
+</body>
+</html>
diff --git a/diff.html b/diff.html
new file mode 100644 (file)
index 0000000..28c9922
--- /dev/null
+++ b/diff.html
@@ -0,0 +1,226 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Speedometer 3 Status Dashboard</title>
+
+  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
+
+  <script>
+    function round(number, decimals) {
+      return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
+    }
+
+    function getDiffColor(diff) {
+      if (diff < -0.05) {
+        return 'green'
+      } else if (diff < 0.095) {
+        return 'black'
+      } else if (diff < 0.2) {
+        return '#b38f00'
+      } else {
+        return 'red'
+      }
+    }
+
+    function loadChart() {
+      fetch('https://sql.telemetry.mozilla.org/api/queries/91105/results.json?api_key=l1VW7NobZMxWLDNpLHMYMMcLsBKFSeeoWBZi0gpZ')
+        .then(function (response) {
+          return response.json();
+        })
+        .then(function (data) {
+          displayChart(data);
+        })
+        .catch(function (err) {
+          console.log('error: ' + err);
+        });
+    }
+
+    function getPlatformWeight(platform) {
+      switch (platform) {
+        case 'windows10-64-shippable-qr':
+          return 0.8;
+        case 'macosx1015-64-shippable-qr':
+          return 0.2;
+        default:
+          return 0.0;
+      }
+    }
+
+    function displayChart(data) {
+      const ctxDesktop = document.getElementById('chartDesktop').getContext('2d');
+
+      var weightedValuesDesktop = {};
+      var weightedValuesMobile = {};
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform == 'android-hw-a51-11-0-aarch64-shippable-qr' || row.difference == null) {
+          return;
+        }
+
+        if (!weightedValuesDesktop.hasOwnProperty(row.push_timestamp)) {
+          weightedValuesDesktop[row.push_timestamp] = {};
+          weightedValuesDesktop[row.push_timestamp]['diff'] = 0;
+          weightedValuesDesktop[row.push_timestamp]['totalWeight'] = 0;
+        }
+
+        weightedValuesDesktop[row.push_timestamp]['diff'] += getPlatformWeight(row.platform) * -row.moving_difference;
+        weightedValuesDesktop[row.push_timestamp]['totalWeight'] += getPlatformWeight(row.platform);
+      });
+
+      data.query_result.data.rows.forEach(function (row) {
+        if (row.platform != 'android-hw-a51-11-0-aarch64-shippable-qr' || row.difference == null) {
+          return;
+        }
+
+        if (!weightedValuesMobile.hasOwnProperty(row.push_timestamp)) {
+          weightedValuesMobile[row.push_timestamp] = {};
+          weightedValuesMobile[row.push_timestamp]['diff'] = 0;
+          weightedValuesMobile[row.push_timestamp]['totalWeight'] = 0;
+        }
+
+        weightedValuesMobile[row.push_timestamp]['diff'] += -row.moving_difference;
+        weightedValuesMobile[row.push_timestamp]['totalWeight'] += 1.0;
+      });
+
+      let xyValues = [];
+      let xyValuesMobile = [];
+      let xyValuesTrend = [];
+
+      for (const timestamp in weightedValuesDesktop) {
+        if (weightedValuesDesktop[timestamp]['totalWeight'] > 0.999) {
+          xyValues.push({ x: timestamp, y: (weightedValuesDesktop[timestamp]['diff']) * 100 });
+        }
+      }
+
+
+      for (const timestamp in weightedValuesMobile) {
+        if (weightedValuesMobile[timestamp]['totalWeight'] > 0.999) {
+          xyValuesMobile.push({ x: timestamp, y: (weightedValuesMobile[timestamp]['diff']) * 100 });
+        }
+      }
+
+      new Chart("chartDesktop", {
+        type: "line",
+        data: {
+          datasets: [
+            {
+              label: 'Difference',
+              data: xyValues
+            }
+          ]
+        },
+        options: {
+          aspectRatio: 2,
+          plugins: { legend: { display: false } },
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              },
+              min: '2023-04-14'
+
+            },
+            y: {
+              beginAtZero: true,
+              title: {
+                display: true,
+                text: 'Performance Difference (%)'
+              },
+            }
+          }
+        }
+      });
+
+      new Chart("chartMobile", {
+        type: "line",
+        data: {
+          datasets: [
+            {
+              label: 'Difference',
+              data: xyValuesMobile
+            },
+          ]
+        },
+        options: {
+          aspectRatio: 2,
+          plugins: { legend: { display: false } },
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              },
+              min: '2023-04-14'
+            },
+            y: {
+              beginAtZero: true,
+              title: {
+                display: true,
+                text: 'Performance Difference (%)'
+              },
+            }
+          }
+        }
+      });
+    }
+
+  </script>
+  <style>
+    h2 {
+      font-family: sans-serif;
+    }
+
+    th {
+      font-family: sans-serif;
+      font-size: 22px;
+      text-align: center;
+      width: 180px;
+    }
+
+    td {
+      font-family: sans-serif;
+      font-size: 20px;
+      text-align: center;
+    }
+
+    .buttons td {
+      width: 200px;
+    }
+
+    a {
+      padding-left: 50px;
+      padding-right: 50px;
+      padding-top: 10px;
+      padding-bottom: 10px;
+      text-decoration: none;
+      color: black;
+      border-radius: 5px;
+    }
+
+      a:hover {
+        background-color: lightgray;
+      }
+  </style>
+</head>
+
+<body onload="loadChart();">
+  <center>
+
+    <h2>Desktop</h2>
+
+    <div style="width:100%;max-width:900px">
+      <canvas id="chartDesktop" style="width:100%;max-width:900px"></canvas>
+    </div>
+
+    <h2>Mobile</h2>
+
+    <div style="width:100%;max-width:900px">
+      <canvas id="chartMobile" style="width:100%;max-width:900px"></canvas>
+    </div>
+
+  </center>
+</body>
+</html>
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..c923628
--- /dev/null
@@ -0,0 +1,616 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Speedometer 3 Status Dashboard</title>
+
+  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
+
+  <script>
+    var chartMobile;
+    var chartDesktop;
+
+    function round(number, decimals) {
+      return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
+    }
+
+    function getDiffColor(diff) {
+      if (diff < -0.05) {
+        return 'green'
+      } else if (diff < 0.095) {
+        return 'black'
+      } else if (diff < 0.2) {
+        return '#b38f00'
+      } else {
+        return 'red'
+      }
+    }
+
+    function loadDataLikely(data) {
+      let arithmeticMeanOfLogsFX = {};
+      let arithmeticMeanOfLogsChrome = {};
+      let arithmeticMeanOfLogsCaR = {};
+      let numValues = {};
+      data.query_result.data.rows.forEach(function (row) {
+        if (!arithmeticMeanOfLogsFX.hasOwnProperty(row.platform)) {
+          arithmeticMeanOfLogsFX[row.platform] = 0;
+          arithmeticMeanOfLogsChrome[row.platform] = 0;
+          arithmeticMeanOfLogsCaR[row.platform] = 0;
+          numValues[row.platform] = 0;
+        }
+        numValues[row.platform]++;
+        arithmeticMeanOfLogsFX[row.platform] += Math.log(row.firefoxmean);
+        arithmeticMeanOfLogsChrome[row.platform] += Math.log(row.chromemean);
+        arithmeticMeanOfLogsCaR[row.platform] += Math.log(row.carmean);
+      });
+
+      let FxGeoMeanDesktop = 0;
+      let ChromeGeoMeanDesktop = 0
+      let CaRGeoMeanDesktop = 0
+      for (const platform in arithmeticMeanOfLogsFX) {
+        FxGeoMeanDesktop += Math.exp(arithmeticMeanOfLogsFX[platform] / numValues[platform]) * getPlatformWeight(platform);
+        ChromeGeoMeanDesktop += Math.exp(arithmeticMeanOfLogsChrome[platform] / numValues[platform]) * getPlatformWeight(platform);
+        CaRGeoMeanDesktop += Math.exp(arithmeticMeanOfLogsCaR[platform] / numValues[platform]) * getPlatformWeight(platform);
+      }
+
+      let FxGeoMeanMobile = Math.exp(arithmeticMeanOfLogsFX['android-hw-a51-11-0-aarch64-shippable-qr'] /
+                            numValues['android-hw-a51-11-0-aarch64-shippable-qr'])
+      let ChromeGeoMeanMobile = Math.exp(arithmeticMeanOfLogsChrome['android-hw-a51-11-0-aarch64-shippable-qr'] /
+                                numValues['android-hw-a51-11-0-aarch64-shippable-qr'])
+      let CaRGeoMeanMobile = Math.exp(arithmeticMeanOfLogsCaR['android-hw-a51-11-0-aarch64-shippable-qr'] /
+        numValues['android-hw-a51-11-0-aarch64-shippable-qr'])
+      let diffDesktop = FxGeoMeanDesktop / ChromeGeoMeanDesktop - 1;
+      let diffMobile = (FxGeoMeanMobile / ChromeGeoMeanMobile) - 1;
+      let diffDesktopCaR = FxGeoMeanDesktop / CaRGeoMeanDesktop - 1;
+      let diffMobileCaR = (FxGeoMeanMobile / CaRGeoMeanMobile) - 1;
+
+      document.getElementById("firefoxTimeDesktop").innerHTML += round(FxGeoMeanDesktop, 2) + ' ms';
+      document.getElementById("chromeTimeDesktop").innerHTML += round(ChromeGeoMeanDesktop, 2) + ' ms';
+      document.getElementById("carTimeDesktop").innerHTML += round(CaRGeoMeanDesktop, 2) + ' ms';
+      document.getElementById("firefoxTimeMobile").innerHTML += round(FxGeoMeanMobile, 2) + ' ms';
+      document.getElementById("chromeTimeMobile").innerHTML += round(ChromeGeoMeanMobile, 2) + ' ms';
+      document.getElementById("carTimeMobile").innerHTML += round(CaRGeoMeanMobile, 2) + ' ms';
+
+      document.getElementById("KRscoreDesktop").innerHTML += '<p style="margin:0px;padding:0;color:' + getDiffColor(diffDesktop) + ';">' + -round(diffDesktop * 100, 1) + '%</p>';
+      document.getElementById("KRscoreMobile").innerHTML += '<p style="margin:0px;padding:0;color:' + getDiffColor(diffMobile) + ';">' + -round(diffMobile * 100, 1) + '%</p>';
+      document.getElementById("futScoreDesktop").innerHTML += '<p style="margin:0px;padding:0;color:' + getDiffColor(diffDesktopCaR) + ';">' + -round(diffDesktopCaR * 100, 1) + '%</p>';
+      document.getElementById("futScoreMobile").innerHTML += '<p style="margin:0px;padding:0;color:' + getDiffColor(diffMobileCaR) + ';">' + -round(diffMobileCaR * 100, 1) + '%</p>';
+    }
+
+    fetch('https://sql.telemetry.mozilla.org/api/queries/89943/results.json?api_key=8pVTrtmbttRWqE5MGuCplgHncRFHON0JUj5zCSGh')
+      .then(function (response) {
+        return response.json();
+      })
+      .then(function (data) {
+        loadDataLikely(data);
+      })
+      .catch(function (err) {
+        console.log('error: ' + err);
+      });
+
+    function loadChart() {
+      fetch('https://sql.telemetry.mozilla.org/api/queries/96199/results.json?api_key=DtrYOBSoDrUaeuxRfvXPdlDiOS5sqrbsGcXyM5Q2')
+        .then(function (response) {
+          return response.json();
+        })
+        .then(function (data) {
+          displayChart(data);
+        })
+        .catch(function (err) {
+          console.log('error: ' + err);
+        });
+    }
+
+    function getPlatformWeight(platform) {
+      switch (platform) {
+        case 'windows10-64-shippable-qr':
+          return 0.8;
+        case 'macosx1015-64-shippable-qr':
+          return 0.2;
+        default:
+          return 0.0;
+      }
+    }
+
+    function displayChart(data) {
+      const ctxDesktop = document.getElementById('chartDesktop').getContext('2d');
+      const ctxMobile = document.getElementById('chartMobile').getContext('2d');
+
+      var weightedValuesDesktop = {};
+      var weightedValuesMobile = {};
+      data.query_result.data.rows.forEach(function (row) {
+        if (!weightedValuesDesktop.hasOwnProperty(row.push_timestamp)) {
+          weightedValuesDesktop[row.push_timestamp] = {};
+        }
+        if (!weightedValuesDesktop[row.push_timestamp].hasOwnProperty(row.application)) {
+          weightedValuesDesktop[row.push_timestamp][row.application] = {};
+          weightedValuesDesktop[row.push_timestamp][row.application]['value'] = 0;
+          weightedValuesDesktop[row.push_timestamp][row.application]['totalWeight'] = 0;
+        }
+
+        weightedValuesDesktop[row.push_timestamp][row.application]['value'] += getPlatformWeight(row.platform) * row.geomean;
+        weightedValuesDesktop[row.push_timestamp][row.application]['totalWeight'] += getPlatformWeight(row.platform);
+        if (row.platform == 'android-hw-a51-11-0-aarch64-shippable-qr') {
+          if (!weightedValuesMobile.hasOwnProperty(row.push_timestamp)) {
+            weightedValuesMobile[row.push_timestamp] = {};
+          }
+          weightedValuesMobile[row.push_timestamp][row.application] = row.geomean;
+        }
+      });
+
+      let xyValuesFx = [];
+      let xyValuesChrome = [];
+      let xyValuesCaR = [];
+      let xyValuesFenix = [];
+      let xyValuesChromeM = [];
+      let xyValuesCaRM = [];
+      for (const timestamp in weightedValuesDesktop) {
+        // May need hack to remove values where a run failed.
+        
+        if (weightedValuesDesktop[timestamp].hasOwnProperty('firefox')) {
+          if (weightedValuesDesktop[timestamp]['firefox']['value'] < 200 && weightedValuesDesktop[timestamp]['firefox']['totalWeight'] > 0.999) {
+            xyValuesFx.push({ x: timestamp, y: weightedValuesDesktop[timestamp]['firefox']['value'] });
+          }
+        }
+        if (weightedValuesDesktop[timestamp].hasOwnProperty('chrome')) {
+          if (weightedValuesDesktop[timestamp]['chrome']['value'] < 145 && weightedValuesDesktop[timestamp]['chrome']['totalWeight'] > 0.999) {
+            xyValuesChrome.push({ x: timestamp, y: weightedValuesDesktop[timestamp]['chrome']['value'] });
+          }
+        }
+        if (weightedValuesDesktop[timestamp].hasOwnProperty('custom-car')) {
+          if (weightedValuesDesktop[timestamp]['custom-car']['value'] < 145 && weightedValuesDesktop[timestamp]['custom-car']['totalWeight'] > 0.999) {
+            xyValuesCaR.push({ x: timestamp, y: weightedValuesDesktop[timestamp]['custom-car']['value'] });
+          }
+        }
+      }
+
+      for (const timestamp in weightedValuesMobile) {
+        if (weightedValuesMobile[timestamp].hasOwnProperty('fenix')) {
+          if (weightedValuesMobile[timestamp]['fenix'] < 950) {
+            xyValuesFenix.push({ x: timestamp, y: weightedValuesMobile[timestamp]['fenix'] });
+          }
+        }
+        if (weightedValuesMobile[timestamp].hasOwnProperty('chrome-m')) {
+          xyValuesChromeM.push({ x: timestamp, y: weightedValuesMobile[timestamp]['chrome-m'] });
+        }
+        if (weightedValuesMobile[timestamp].hasOwnProperty('cstm-car-m')) {
+          xyValuesCaRM.push({ x: timestamp, y: weightedValuesMobile[timestamp]['cstm-car-m'] });
+        }
+      }
+
+      chartDesktop = new Chart("chartDesktop", {
+        type: "scatter",
+        data: {
+          datasets: [
+            {
+              label: 'Firefox',
+              pointRadius: 3,
+              pointBackgroundColor: "#FF9500",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesFx
+            },
+            {
+              label: 'Chrome',
+              pointRadius: 3,
+              pointBackgroundColor: "#1DA462",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesChrome
+            },
+            {
+              label: 'Chromium-as-Release',
+              pointRadius: 3,
+              pointBackgroundColor: "#0099ff",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesCaR
+            }
+          ]
+        },
+        options: {
+          aspectRatio: 1.5,
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              }
+            },
+            y: {
+              beginAtZero: true
+            }
+          },
+          plugins: {
+            annotation: {
+              annotations: {
+                line4: {
+                  type: 'line',
+                  xMin: '2023-04-14 08:00',
+                  xMax: '2023-04-14 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label4: {
+                  type: 'label',
+                  xValue: '2023-04-14 08:00',
+                  yValue: 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Svelte Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line5: {
+                  type: 'line',
+                  xMin: '2023-06-07 00:00',
+                  xMax: '2023-06-07 00:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label5: {
+                  type: 'label',
+                  xValue: '2023-06-07 00:00',
+                  yValue: 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Multiple Tests Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line6: {
+                  type: 'line',
+                  xMin: '2023-06-15 08:00',
+                  xMax: '2023-06-15 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label6: {
+                  type: 'label',
+                  xValue: '2023-06-15 08:00',
+                  yValue: 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Perf-Dashboard Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line7: {
+                  type: 'line',
+                  xMin: '2023-07-04 21:00',
+                  xMax: '2023-07-04 21:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label7: {
+                  type: 'label',
+                  xValue: '2023-07-04 21:00',
+                  yValue: 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['rAF Switch/Tests Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line8: {
+                  type: 'line',
+                  xMin: '2023-12-30 17:00',
+                  xMax: '2023-12-30 17:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label8: {
+                  type: 'label',
+                  xValue: '2023-12-30 17:00',
+                  yValue: 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Complex-DOM Workloads/Freeze'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line9: {
+                  type: 'line',
+                  xMin: '2024-03-09 00:00',
+                  xMax: '2024-03-09 00:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label9: {
+                  type: 'label',
+                  xValue: '2024-03-09 00:00',
+                  yValue: 40,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Release'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+              }
+            }
+          }
+        }
+      });
+      chartMobile = new Chart("chartMobile", {
+        type: "scatter",
+        data: {
+          datasets: [
+            {
+              label: 'Fenix',
+              pointRadius: 3,
+              pointBackgroundColor: "#FF9500",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesFenix
+            },
+            {
+              label: 'Chrome',
+              pointRadius: 3,
+              pointBackgroundColor: "#1DA462",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesChromeM
+            },
+            {
+              label: 'Chromium-as-Release',
+              pointRadius: 3,
+              pointBackgroundColor: "#0099ff",
+              pointBorderColor: "#000000",
+              pointBorderWidth: 0.5,
+              data: xyValuesCaRM
+            }
+          ]
+        },
+        options: {
+          aspectRatio: 1.5,
+          scales: {
+            x: {
+              type: 'time',
+              time: {
+                unit: 'week'
+              }
+            },
+            y: {
+              beginAtZero: true
+            }
+          },
+          plugins: {
+            annotation: {
+              annotations: {
+                line4: {
+                  type: 'line',
+                  xMin: '2023-04-14 08:00',
+                  xMax: '2023-04-14 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label4: {
+                  type: 'label',
+                  xValue: '2023-04-14 08:00',
+                  yValue: 200,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Svelte Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line5: {
+                  type: 'line',
+                  xMin: '2023-06-07 00:00',
+                  xMax: '2023-06-07 00:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label5: {
+                  type: 'label',
+                  xValue: '2023-06-07 00:00',
+                  yValue: 200,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Multiple Tests Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line6: {
+                  type: 'line',
+                  xMin: '2023-06-15 08:00',
+                  xMax: '2023-06-15 08:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label6: {
+                  type: 'label',
+                  xValue: '2023-06-15 08:00',
+                  yValue: 200,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Perf-Dashboard Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line7: {
+                  type: 'line',
+                  xMin: '2023-07-04 21:00',
+                  xMax: '2023-07-04 21:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label7: {
+                  type: 'label',
+                  xValue: '2023-07-04 21:00',
+                  yValue: 200,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['rAF Switch/Tests Added'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line8: {
+                  type: 'line',
+                  xMin: '2023-12-30 17:00',
+                  xMax: '2023-12-30 17:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label8: {
+                  type: 'label',
+                  xValue: '2023-12-30 17:00',
+                  yValue: 200,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Complex-DOM Workloads/Freeze'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+                line9: {
+                  type: 'line',
+                  xMin: '2024-03-09 00:00',
+                  xMax: '2024-03-09 00:00',
+                  borderColor: 'black',
+                  borderWidth: 2,
+                },
+                label9: {
+                  type: 'label',
+                  xValue: '2024-03-09 00:00',
+                  yValue: 200,
+                  backgroundColor: 'rgb(255,128,128)',
+                  content: ['Release'],
+                  font: {
+                    size: 14
+                  },
+                  rotation: -90,
+                  borderRadius: 4
+                },
+              }
+            }
+          }
+        }
+      });
+    }
+
+
+    function changeRange(newRange) {
+      if (newRange == 'all') {
+        chartMobile.options.scales.x.min = '';
+        chartDesktop.options.scales.x.min = '';
+      } else {
+        var d = new Date();
+        d.setMonth(d.getMonth() - newRange);
+        chartMobile.options.scales.x.min = d;
+        chartDesktop.options.scales.x.min = d;
+      }
+      chartMobile.update();
+      chartDesktop.update();
+    }
+  </script>
+  <style>
+    h2 {
+      font-family: sans-serif;
+    }
+
+    th {
+      font-family: sans-serif;
+      font-size: 22px;
+      text-align: center;
+      width:180px;
+    }
+
+    td {
+      font-family: sans-serif;
+      font-size: 20px;
+      text-align: center;
+    }
+
+    .buttons td {
+      width:250px;
+    }
+
+    a {
+      padding-left:30px;
+      padding-right:30px;
+      padding-top:10px;
+      padding-bottom:10px;
+      text-decoration:none;
+      color:black;
+      border-radius:5px;
+    }
+    a:hover {
+      background-color:lightgray;
+    }
+  </style>
+</head>
+
+<body onload="loadChart();">
+  <center>
+    <table class="buttons">
+      <tr><td colspan="6" style="height:80px;"><a href="index.html" style="background-color:gray;">Overall</a></td></tr>
+      <tr><td colspan="3"><a href="details.html" id="windowsbutton">Windows</a></td><td colspan="3"><a href="details.html?os=linux" id="linuxbutton">Linux</a></td></tr>
+      <tr><td colspan="3"><a href="details.html?os=osx" id="macbutton">Mac OSX</a></td><td colspan="3" style="height:50px;"><a href="details.html?os=osxm2" id="osxm2button">Mac OSX (M2)</a></td></tr>
+      <tr><td colspan="2" style="height:50px;"><a href="details.html?os=android" id="mobilebutton">Android (A51)</a></td><td colspan="2" style="height:50px;"><a href="details.html?os=android-s21" id="mobilebutton">Android (S21)</a></td><td colspan="2" style="height:50px;"><a href="details.html?os=android" id="mobilebutton">Android (Pixel 6)</a></td></tr>
+    </table>
+
+    <table>
+      <tr><th colspan="2">KR Score</th></tr>
+      <tr><th>Desktop</th><th>Mobile</th></tr>
+      <tr><td id="KRscoreDesktop"></td><td id="KRscoreMobile"></td></tr>
+    </table>
+    <br />
+    <table>
+      <tr><th colspan="2">Future Score (vs CaR)</th></tr>
+      <tr><th>Desktop</th><th>Mobile</th></tr>
+      <tr><td id="futScoreDesktop"></td><td id="futScoreMobile"></td></tr>
+    </table>
+    <br />
+    <table class="buttons">
+      <tr><td><a onclick="changeRange('all')" id="allbutton">All time</a></td><td><a onclick="changeRange(3)" id="month3button">3 months</a></td><td><a onclick="changeRange(1)" id="month1button">1 month</a></td></tr>
+    </table>
+
+    <h2>Desktop Status (lower is better)</h2>
+
+    <table>
+      <tr><th>Firefox</th><th>Chrome</th><th>Chromium-as-Release</th></tr>
+      <tr><td id="firefoxTimeDesktop"></td><td id="chromeTimeDesktop"></td><td id="carTimeDesktop"></td></tr>
+    </table>
+
+    <div style="width:100%;max-width:900px">
+      <canvas id="chartDesktop" style="width:100%;max-width:900px"></canvas>
+    </div>
+
+    <br />
+    <h2>Mobile Status (lower is better)</h2>
+
+    <table>
+      <tr><th>Firefox</th><th>Chrome</th><th>Chromium-as-Release</th></tr>
+      <tr><td id="firefoxTimeMobile"></td><td id="chromeTimeMobile"></td><td id="carTimeMobile"></td></tr>
+    </table>
+
+    <div style="width:100%;max-width:900px">
+      <canvas id="chartMobile" style="width:100%;max-width:900px"></canvas>
+    </div>
+  </center>
+</body>
+</html>