diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b99a9c099e01b288f3d930f9e067c739406c233..604e7219d8c18a83d23bbe56b422d4ac5ddc8bc8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,8 @@
     name.
 * Added additional argument to `example` shortcode to turn off the copy button.
 * Fixed pseudocode not rendering properly due to element `p` overflow setting.
+* Added the ability for the `git-heatmap` to combine activity from multiple
+    sources.
 
 ## 2.1.0
 
diff --git a/hugo.toml.example b/hugo.toml.example
index 4bd94ce9dbd5687d348b644a2a810055e35b72fc..3580257ab16674e03d1b07fa578be489dfae62d4 100644
--- a/hugo.toml.example
+++ b/hugo.toml.example
@@ -42,4 +42,4 @@ siteEmail="the@jeffreyfreeman.me"
 postHashtags=["Fediverse", "ActivityPub", "MastoAdmin"]
 renderDefaultHashtags=true
 renderArticleHashtags=true
-gitlabHeatmapData="https://git.qoto.org/users/freemo/calendar.json"
+gitlabHeatmapData=["https://corsproxy.io?https://gitlab.com/users/freemo/calendar.json", "https://git.qoto.org/users/freemo/calendar.json"]
diff --git a/layouts/partials/head.html b/layouts/partials/head.html
index 3b090f4d6854e7d18d7f24896d9687bd6c87fac5..0ce9bf10b2cc3f8d4b4eb76b02d0a32dc42d0710 100644
--- a/layouts/partials/head.html
+++ b/layouts/partials/head.html
@@ -192,81 +192,96 @@ document.addEventListener("DOMContentLoaded", function() {
       return xhr.response
   };
 
-
   // Get the data as a String of JSON
-  const data = JSON.parse(getJSON('{{ .Site.Params.gitlabHeatmapData }}'));
+  const dataSources = {{ .Site.Params.gitlabHeatmapData }};
+  if (dataSources.length > 0) {
+    const data = JSON.parse(getJSON(dataSources[0]));
+    // Convert string to JSON
+    const dataMap = new Map(Object.entries(data));
 
-  // Convert string to JSON
-  const dataMap = new Map(Object.entries(data));
+    for ( let i = 1; i < dataSources.length ; i++) {
+      let dataNew = JSON.parse(getJSON(dataSources[i]));
+      let dataMapNew = new Map(Object.entries(dataNew));
 
-  //convert JSON to an array of maps
-  const newData = []
-  for( let [entry, value] of dataMap ) {
-    newData.push({"date": entry, "value": value});
-  }
+      for (let [key, value] of dataMapNew) {
+        let valueOld = dataMap.get(key);
+        if( valueOld ) {
+          dataMap.set(key, valueOld + value);
+        } else {
+          dataMap.set(key, value);
+        }
+      }
+    }
 
-  const d = new Date();
-  const lastYear = d.setMonth(d.getMonth() - 11);
+    //convert JSON to an array of maps
+    const newData = []
+    for( let [entry, value] of dataMap ) {
+      newData.push({"date": entry, "value": value});
+    }
 
-  // Now render it.
-  const cal = new CalHeatmap();
-  cal.paint({
-    data: {
-      source: newData,
-      type: 'json',
-      x: 'date',
-      y: 'value',
-      groupY: 'max',
-    },
-    date: { start: lastYear },
-    range: 12,
-    scale: {
-      color: {
-        type: 'threshold',
-        range: ['#0000FF', '#3000CF', '#60009F', '#90006F', '#C0003F', '#F0000F', '#FF0000'],
-        domain: [2, 3, 5, 8, 13, 21],
-      },
-    },
-    domain: {
-      type: 'month',
-      gutter: 4,
-      label: { text: 'MMM', textAlign: 'start', position: 'top' },
-    },
-    subDomain: { type: 'ghDay', radius: 2, width: 11, height: 11, gutter: 4 },
-  },
-  [
-    [
-      Legend,
-      {
-        tickSize: 1,
-        width: 100,
-        itemSelector: '#cal-heatmap-legend',
-        label: 'Activity per day',
+    const d = new Date();
+    const lastYear = d.setMonth(d.getMonth() - 11);
+
+    // Now render it.
+    const cal = new CalHeatmap();
+    cal.paint({
+      data: {
+        source: newData,
+        type: 'json',
+        x: 'date',
+        y: 'value',
+        groupY: 'max',
       },
-    ],
-    [
-      Tooltip,
-      {
-        text: function (date, value, dayjsDate) {
-          return (
-            (value ? value : 'No') +
-            ' contributions on ' +
-            dayjsDate.format('dddd, MMMM D, YYYY')
-          );
+      date: { start: lastYear },
+      range: 12,
+      scale: {
+        color: {
+          type: 'threshold',
+          range: ['#0000FF', '#3000CF', '#60009F', '#90006F', '#C0003F', '#F0000F', '#FF0000'],
+          domain: [2, 3, 5, 8, 13, 21],
         },
       },
-    ],
-    [
-      CalendarLabel,
-      {
-        width: 30,
-        textAlign: 'start',
-        text: () => dayjs.weekdaysShort().map((d, i) => (i % 2 == 0 ? '' : d)),
-        padding: [25, 0, 0, 0],
+      domain: {
+        type: 'month',
+        gutter: 4,
+        label: { text: 'MMM', textAlign: 'start', position: 'top' },
       },
-    ],
-  ]
-);
+      subDomain: { type: 'ghDay', radius: 2, width: 11, height: 11, gutter: 4 },
+    },
+    [
+      [
+        Legend,
+        {
+          tickSize: 1,
+          width: 100,
+          itemSelector: '#cal-heatmap-legend',
+          label: 'Activity per day',
+        },
+      ],
+      [
+        Tooltip,
+        {
+          text: function (date, value, dayjsDate) {
+            return (
+              (value ? value : 'No') +
+              ' contributions on ' +
+              dayjsDate.format('dddd, MMMM D, YYYY')
+            );
+          },
+        },
+      ],
+      [
+        CalendarLabel,
+        {
+          width: 30,
+          textAlign: 'start',
+          text: () => dayjs.weekdaysShort().map((d, i) => (i % 2 == 0 ? '' : d)),
+          padding: [25, 0, 0, 0],
+        },
+      ],
+    ]
+  );
+}
 
 </script>