diff --git a/src/hall-of-game/index.ts b/src/hall-of-game/index.ts
new file mode 100644
index 00000000..c9a49809
--- /dev/null
+++ b/src/hall-of-game/index.ts
@@ -0,0 +1,10 @@
+import fp from 'fastify-plugin'
+
+export default fp(
+ async app => {
+ await app.register((await import('./routes')).default)
+ },
+ {
+ name: 'hall of fame',
+ },
+)
diff --git a/src/hall-of-game/routes.ts b/src/hall-of-game/routes.ts
new file mode 100644
index 00000000..61c1d086
--- /dev/null
+++ b/src/hall-of-game/routes.ts
@@ -0,0 +1,13 @@
+import fp from 'fastify-plugin'
+import { HallOfFamePage } from './views/html/hall-of-fame.page'
+
+export default fp(
+ async app => {
+ app.get('/hall-of-fame', async (request, reply) => {
+ reply.status(200).html(await HallOfFamePage({ user: request.user }))
+ })
+ },
+ {
+ name: 'hall of fame routes',
+ },
+)
diff --git a/src/hall-of-game/views/html/hall-of-fame.page.css b/src/hall-of-game/views/html/hall-of-fame.page.css
new file mode 100644
index 00000000..f8d983a9
--- /dev/null
+++ b/src/hall-of-game/views/html/hall-of-fame.page.css
@@ -0,0 +1,63 @@
+.hof-board {
+ display: grid;
+ grid-template-columns: auto auto 1fr auto;
+ column-gap: 16px;
+ row-gap: 10px;
+
+ padding: 10px 14px;
+
+ background-color: rgba(19, 16, 20, 0.5);
+ color: theme(colors.abru.light.75);
+ border-radius: 8px;
+
+ font-size: 20px;
+ font-weight: 500;
+
+ .title {
+ font-size: 24px;
+ font-weight: 700;
+ }
+
+ .hof-record {
+ @apply transition-colors;
+ @apply duration-75;
+
+ display: grid;
+ grid-column: span 4 / span 4;
+ grid-template-columns: subgrid;
+ border-radius: 4px;
+ align-items: center;
+ padding: 0px 8px 0px 0px;
+
+ &:hover {
+ background-color: theme(colors.abru.light.5 / 40%);
+ }
+
+ &.is-1st,
+ &.is-2nd,
+ &.is-3rd {
+ padding: 10px 8px 10px 10px;
+ }
+
+ &.is-1st {
+ background-color: theme(colors.abru.light.15);
+ &:hover {
+ background-color: darken(theme(colors.abru.light.15), 2%);
+ }
+ }
+
+ &.is-2nd {
+ background-color: theme(colors.abru.light.10);
+ &:hover {
+ background-color: darken(theme(colors.abru.light.10), 2%);
+ }
+ }
+
+ &.is-3rd {
+ background-color: theme(colors.abru.light.5);
+ &:hover {
+ background-color: darken(theme(colors.abru.light.5), 2%);
+ }
+ }
+ }
+}
diff --git a/src/hall-of-game/views/html/hall-of-fame.page.tsx b/src/hall-of-game/views/html/hall-of-fame.page.tsx
new file mode 100644
index 00000000..5872f94a
--- /dev/null
+++ b/src/hall-of-game/views/html/hall-of-fame.page.tsx
@@ -0,0 +1,133 @@
+import { resolve } from 'node:path'
+import type { User } from '../../../auth/types/user'
+import { Layout } from '../../../html/layout'
+import { NavigationBar } from '../../../html/components/navigation-bar'
+import { Page } from '../../../html/components/page'
+import { Footer } from '../../../html/components/footer'
+import { collections } from '../../../database/collections'
+import { GameState } from '../../../database/models/game.model'
+import { Tf2ClassName } from '../../../shared/types/tf2-class-name'
+import type { PlayerModel } from '../../../database/models/player.model'
+import { IconAwardFilled } from '../../../html/components/icons'
+
+interface HallOfFameEntry {
+ player: PlayerModel
+ count: number
+}
+
+export async function HallOfFamePage(props: { user?: User | undefined }) {
+ const [all, medics] = await Promise.all([getMostActiveOverall(), getMostActiveMedics()])
+
+ return (
+