diff --git a/README.md b/README.md
index dd16fa8c748b64731973d4f5c1f0453526f922c8..a2b051e2779c71e8524f49bf2892de24413e950b 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ If you dont have access to the `Sitzungen` Repository, you can use the `local_te
 To stay consistent in e.g. spacing and have the chance to track changes, please format the files with
 
 ```
-pnpm formt
+pnpm format
 ```
 
 in the root of the repository.
diff --git a/src/lib/attendance.ts b/src/lib/attendance.ts
index 66f53805fb149fbf39fd9a058c6a0ab018116f34..e84a290b84f101f8945abc9ef00d3b9368838240 100644
--- a/src/lib/attendance.ts
+++ b/src/lib/attendance.ts
@@ -1,6 +1,36 @@
 import fs from "node:fs/promises";
-import { FinishedTranscriptMeta, Resolution } from "./parsing";
-import { Attendance, generateAttendanceHtmlWrapper } from "./rendering";
+import { FinishedTranscriptMeta } from "./parsing";
+import { generateAttendanceHtmlWrapper } from "./rendering";
+
+export class Attendance {
+    presents: number[] = [];
+    noshows: number[] = [];
+    noshow_excuseds: number[] = [];
+
+    get present(): number {
+        return this.presents.length;
+    }
+
+    get noshow(): number {
+        return this.noshows.length;
+    }
+
+    get noshow_excused(): number {
+        return this.noshow_excuseds.length;
+    }
+
+    sum(): number {
+        return this.present + this.noshow + this.noshow_excused;
+    }
+
+    union(other: Attendance) {
+        this.presents = this.presents.concat(other.presents).sort();
+        this.noshows = this.noshows.concat(other.noshows).sort();
+        this.noshow_excuseds = this.noshow_excuseds
+            .concat(other.noshow_excuseds)
+            .sort();
+    }
+}
 
 export async function generateAttendancePage(
     outputDir: string,
@@ -49,25 +79,25 @@ function _generateAttendancePart(
 ): Map<string, Attendance> {
     const attendanceMap = new Map<string, Attendance>();
 
-    for (const { present, absent } of relevantTranscripts) {
+    for (const { present, absent, number } of relevantTranscripts) {
         present.forEach((name) =>
-            _incrementAttendance(name, attendanceMap, true),
+            _incrementAttendance(name, attendanceMap, true, number!),
         );
         absent.forEach((name) =>
-            _incrementAttendance(name, attendanceMap, false),
+            _incrementAttendance(name, attendanceMap, false, number!),
         );
     }
 
-    _mergeAttendance(attendanceMap);
+    _mergeAttendance(attendanceMap, relevantTranscripts.length);
 
     return attendanceMap;
 }
 
-function _mergeAttendance(attendanceMap: Map<string, Attendance>) {
+function _mergeAttendance(
+    attendanceMap: Map<string, Attendance>,
+    maxAttendance: number,
+) {
     const names: string[] = [...attendanceMap.keys()];
-    const maxAttendance: number = Math.max(
-        ...[...attendanceMap.values()].map(_sumAttendance),
-    );
 
     for (const shortName of names) {
         if (shortName.includes(" ")) {
@@ -83,16 +113,13 @@ function _mergeAttendance(attendanceMap: Map<string, Attendance>) {
             const longAttendance: Attendance = attendanceMap.get(
                 nameMatches[0],
             )!;
-            longAttendance.present += shortAttendance.present;
-            longAttendance.noshow += shortAttendance.noshow;
-            longAttendance.noshow_excused += shortAttendance.noshow_excused;
+            longAttendance.union(shortAttendance);
             attendanceMap.delete(shortName);
         }
     }
 
     let missingNames = [...attendanceMap.keys()].filter(
-        (name: string) =>
-            _sumAttendance(attendanceMap.get(name)!) != maxAttendance,
+        (name: string) => attendanceMap.get(name)!.sum() != maxAttendance,
     );
     for (const missingName of missingNames) {
         const missingAttendance = attendanceMap.get(missingName);
@@ -112,15 +139,12 @@ function _mergeAttendance(attendanceMap: Map<string, Attendance>) {
             const otherName = potentialMatches.pop()!;
             const otherAttendance = attendanceMap.get(otherName)!;
             if (
-                _sumAttendance(missingAttendance)
-                    + _sumAttendance(otherAttendance)
+                missingAttendance.sum() + otherAttendance.sum()
                 > maxAttendance
             ) {
                 continue;
             }
-            missingAttendance.present += otherAttendance.present;
-            missingAttendance.noshow += otherAttendance.noshow;
-            missingAttendance.noshow_excused += otherAttendance.noshow_excused;
+            missingAttendance.union(otherAttendance);
             attendanceMap.delete(otherName);
             missingNames = missingNames.filter((e) => e != otherName);
         }
@@ -131,30 +155,24 @@ function _incrementAttendance(
     name: string,
     attendanceMap: Map<string, Attendance>,
     present: boolean,
+    transscriptNumber: number,
 ) {
     const cleanName = _cleanName(name);
-    const attendance: Attendance = attendanceMap.get(cleanName) ?? {
-        present: 0,
-        noshow: 0,
-        noshow_excused: 0,
-    };
+    const attendance: Attendance =
+        attendanceMap.get(cleanName) ?? new Attendance();
 
     if (present) {
-        attendance.present += 1;
+        attendance.presents.push(transscriptNumber);
     } else {
         if (_isExcused(name)) {
-            attendance.noshow_excused += 1;
+            attendance.noshow_excuseds.push(transscriptNumber);
         } else {
-            attendance.noshow += 1;
+            attendance.noshows.push(transscriptNumber);
         }
     }
     attendanceMap.set(cleanName, attendance);
 }
 
-function _sumAttendance(attendance: Attendance): number {
-    return attendance.present + attendance.noshow_excused + attendance.noshow;
-}
-
 function _isExcused(name: string): boolean {
     return name.search(/([(\[]).*?(([eE])(ntschuldigt)?).*?([)\]])/) !== -1;
 }
diff --git a/src/lib/rendering.ts b/src/lib/rendering.ts
index f1487f166041ff0a9ae9f63c13390655cfcf460f..0be563737452c59fae3e0f619446815074e000f5 100644
--- a/src/lib/rendering.ts
+++ b/src/lib/rendering.ts
@@ -1,4 +1,5 @@
 import { FinishedTranscriptMeta, Resolution, Todo } from "./parsing";
+import { Attendance } from "./attendance";
 
 export function renderContainerToAlert(context: String, title?: String) {
     return function (tokens, idx) {
@@ -535,12 +536,6 @@ ${resolution.date} ${resolution.number}: beschlossen
     return `<details><summary><small class="text-body-secondary fw-light">Hledger-Statement für die Buchhaltung</small></summary><pre><code id="reso-text-${resolution.number}">${hledgerText}</code></pre><button class="btn btn-outline-primary btn-sm" onclick="navigator.clipboard.writeText(document.getElementById(\'reso-text-${resolution.number}\').innerText)"><svg width="16" height="16" version="2.0"><use href="#clipboard-icon" /></svg> Hledger-Statement kopieren</button></details>`;
 }
 
-export interface Attendance {
-    present: number;
-    noshow: number;
-    noshow_excused: number;
-}
-
 export function generateAttendanceHtmlWrapper(
     attendanceParts: [
         Map<string, Attendance>,
@@ -567,6 +562,22 @@ export function generateAttendanceHtmlWrapper(
             border: 1px solid #ccc;
             margin-top: 5px;
           }
+          body:has(#grouped-bar-toggle:checked) .grouped-bar {
+            visibility: visible;
+            position: static;
+          }
+          body:has(#grouped-bar-toggle:checked) .ungrouped-bar {
+            visibility: hidden;
+            position: absolute;
+          }
+          .grouped-bar {
+            visibility: hidden;
+            position: absolute;
+          }
+          .ungrouped-bar {
+            visibility: visible;
+            position: static;
+          }
         </style>
         <script>
           document.addEventListener('DOMContentLoaded', function () {
@@ -618,24 +629,30 @@ export function generateAttendanceHtmlWrapper(
           <header>
             <nav class="navbar navbar-expand bg-body-tertiary">
               <ul class="navbar-nav">
-              <li class="nav-item">
-                <a class="nav-link" href="../">Protokolle</a>
-                <i class="fa fa-link"></i>    </li>
-              <li class="nav-item">
-                <a class="nav-link" href="../resolutions/">Beschlüsse</a>
-              </li>
-              <!-- WIP:
-              <li class="nav-item">
-                <a class="nav-link" href="../relevant/">Relevante Beschlüsse</a>
-              </li>
-              -->
-              <li class="nav-item">
-                <a class="nav-link" href="../attendance/">Anwesenheit</a>
-              </li>
-              <li class="nav-item">
-                <a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
-              </li>
-            </ul>
+                <li class="nav-item">
+                  <a class="nav-link" href="../">Protokolle</a>
+                  <i class="fa fa-link"></i>    </li>
+                <li class="nav-item">
+                  <a class="nav-link" href="../resolutions/">Beschlüsse</a>
+                </li>
+                <!-- WIP:
+                <li class="nav-item">
+                  <a class="nav-link" href="../relevant/">Relevante Beschlüsse</a>
+                </li>
+                -->
+                <li class="nav-item">
+                  <a class="nav-link" href="../attendance/">Anwesenheit</a>
+                </li>
+                <li class="nav-item">
+                  <a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
+                </li> 
+              </ul>
+              <div class="ms-auto d-flex-flex align-items-center form-check me-3">
+                <input class="form-check-input me-2" type="checkbox" id="grouped-bar-toggle" value="" checked />
+                <label class="form-check-label" for="grouped-bar-toggle">
+                  Group attendance bar
+                </label>
+              </div>
             </nav>
           </header>
         </div>
@@ -689,22 +706,43 @@ export function generateAttendanceHtml(
 }
 
 function generateAttendanceRowHtml(name: string, data: Attendance): string {
-    const sum = (data.present + data.noshow + data.noshow_excused) / 100;
-
-    const bar = `
-      <div class="percentage-bar">
-        <div style="width: ${data.present / sum}%;background-color: darkgreen"></div>
-        <div style="width: ${data.noshow_excused / sum}%;background-color: yellow"></div>
-        <div style="width: ${data.noshow / sum}%;background-color: red"></div>
-      </div>`;
-
     return `
       <tr>
         <td> ${name} </td> 
-        <td> ${bar} </td> 
+        <td> ${generateGroupedPercentageBar(data)} ${generateUngroupedPercentageBar(data)} </td> 
         <td> ${data.present} </td> 
         <td> ${data.noshow_excused} </td> 
         <td> ${data.noshow}</td> 
-        <td> ${(data.present / sum).toFixed(1)}% </td> 
+        <td> ${((data.present * 100) / data.sum()).toFixed(1)}% </td> 
       </tr>`;
 }
+
+function generateUngroupedPercentageBar(data: Attendance): string {
+    const width = 100 / data.sum();
+
+    const tagged: { value: number; color: string }[] = [
+        ...data.presents.map((value) => ({ value, color: "darkgreen" })),
+        ...data.noshows.map((value) => ({ value, color: "red" })),
+        ...data.noshow_excuseds.map((value) => ({ value, color: "yellow" })),
+    ];
+
+    // Sort by number
+    tagged.sort((a, b) => a.value - b.value);
+
+    var barHtml = '<div class="percentage-bar ungrouped-bar">';
+    // Iterate in order
+    for (const { color } of tagged) {
+        barHtml += `<div style="width: ${width}%;background-color: ${color}"></div>`;
+    }
+    barHtml += "</div>";
+    return barHtml;
+}
+
+function generateGroupedPercentageBar(data: Attendance): string {
+    return `
+      <div class="percentage-bar grouped-bar">
+        <div style="width: ${(data.present * 100) / data.sum()}%;background-color: darkgreen"></div>
+        <div style="width: ${(data.noshow_excused * 100) / data.sum()}%;background-color: yellow"></div>
+        <div style="width: ${(data.noshow * 100) / data.sum()}%;background-color: red"></div>
+      </div>`;
+}
diff --git a/test/sitzungen b/test/sitzungen
index bca907530f65c7088b6cd362f2f6f6fff463d05b..4284e35c4f0af500eef8bb6fbbeaa306507625da 160000
--- a/test/sitzungen
+++ b/test/sitzungen
@@ -1 +1 @@
-Subproject commit bca907530f65c7088b6cd362f2f6f6fff463d05b
+Subproject commit 4284e35c4f0af500eef8bb6fbbeaa306507625da