diff --git a/src/lib/components/TrackerIcon.svelte b/src/lib/components/TrackerIcon.svelte
index 1483449..efd5150 100644
--- a/src/lib/components/TrackerIcon.svelte
+++ b/src/lib/components/TrackerIcon.svelte
@@ -1,27 +1,59 @@
-
-
-
+
(failed = true)}
+ />
+
+{:else}
+
+
+
+{/if}
+
+
diff --git a/src/lib/game-logos.ts b/src/lib/game-logos.ts
new file mode 100644
index 0000000..a057224
--- /dev/null
+++ b/src/lib/game-logos.ts
@@ -0,0 +1,106 @@
+// Official game logos for the game tracker: one self-hosted asset per game in
+// static/game-logos/icons/, keyed like `gameTrackers[].icon` (game-). TrackerIcon uses
+// this map everywhere games show up (header flyout, home and /trackers tiles, /start chips,
+// the games switcher and each game page); a game without an entry keeps its tracker-icons tile.
+// Square app icons/emblems are used instead of wide wordmarks so they stay legible at chip size.
+
+export type GameLogo = {
+ /** Path under static/ (no leading slash), resolved with `asset`/`resolve` at render time. */
+ src: string;
+ /** Opaque square app icon: fill the whole tile instead of sitting padded on the dark tile. */
+ bleed?: boolean;
+ /** Where the asset came from (shown on /projects/game-tracker/credits). */
+ credit: string;
+ source: string;
+};
+
+const steamIcon = (appid: number) => `https://store.steampowered.com/app/${appid}/`;
+const commons = (file: string) =>
+ `https://commons.wikimedia.org/wiki/File:${encodeURIComponent(file.replaceAll(' ', '_'))}`;
+
+export const gameLogos: Record = {
+ 'game-osu': {
+ src: 'game-logos/icons/osu.svg',
+ credit: 'osu! logo (2016), Wikimedia Commons',
+ source: commons('Osu! Logo 2016.svg')
+ },
+ 'game-cs2': {
+ src: 'game-logos/icons/cs2.png',
+ bleed: true,
+ credit: 'Counter-Strike 2 app icon, Steam',
+ source: steamIcon(730)
+ },
+ 'game-pubg': {
+ src: 'game-logos/icons/pubg.png',
+ credit: 'PUBG: Battlegrounds icon, pubg.com',
+ source: 'https://pubg.com/'
+ },
+ 'game-r6': {
+ src: 'game-logos/icons/r6.png',
+ bleed: true,
+ credit: 'Rainbow Six Siege app icon, Steam',
+ source: steamIcon(359550)
+ },
+ 'game-tarkov': {
+ src: 'game-logos/icons/tarkov.png',
+ bleed: true,
+ credit: 'Escape from Tarkov app icon, Steam',
+ source: steamIcon(3932890)
+ },
+ 'game-bo2': {
+ src: 'game-logos/icons/bo2.png',
+ bleed: true,
+ credit: 'Call of Duty: Black Ops II app icon, Steam',
+ source: steamIcon(202970)
+ },
+ 'game-valorant': {
+ src: 'game-logos/icons/valorant.svg',
+ credit: 'Valorant logo (V mark, pink), Wikimedia Commons',
+ source: commons('Valorant logo - pink color version.svg')
+ },
+ 'game-lol': {
+ src: 'game-logos/icons/lol.svg',
+ credit: 'League of Legends logomark, Simple Icons (Hextech gold)',
+ source: 'https://simpleicons.org/?q=league+of+legends'
+ },
+ 'game-overwatch': {
+ src: 'game-logos/icons/overwatch.svg',
+ credit: 'Overwatch 2 logo (light emblem for dark tiles), Wikimedia Commons',
+ source: commons('Overwatch 2 logo.svg')
+ },
+ 'game-minecraft': {
+ src: 'game-logos/icons/minecraft.svg',
+ credit: 'Minecraft grass block icon, Wikimedia Commons',
+ source: commons('Minecraft cube.svg')
+ },
+ 'game-factorio': {
+ src: 'game-logos/icons/factorio.png',
+ credit: 'Factorio wheel logo, official Factorio press kit',
+ source: 'https://factorio.com/support/press-kit'
+ },
+ 'game-ksp': {
+ src: 'game-logos/icons/ksp.png',
+ credit: 'Kerbal Space Program app icon, Steam',
+ source: steamIcon(220200)
+ },
+ 'game-smash': {
+ src: 'game-logos/icons/smash.svg',
+ credit: 'Super Smash Bros. emblem, Wikimedia Commons (flame-coloured)',
+ source: commons('Smash Ball.svg')
+ },
+ 'game-cubing': {
+ src: 'game-logos/icons/cubing.svg',
+ credit: 'World Cube Association logo, Wikimedia Commons',
+ source: commons('World Cube Association.svg')
+ },
+ 'game-pokemon': {
+ src: 'game-logos/icons/pokemon.svg',
+ credit: 'Poké Ball icon, Wikimedia Commons',
+ source: commons('Poké Ball icon.svg')
+ },
+ 'game-sot': {
+ src: 'game-logos/icons/sot.png',
+ credit: 'Sea of Thieves app icon (skull), Steam',
+ source: steamIcon(1172620)
+ }
+};
diff --git a/src/lib/tracker-icons.test.ts b/src/lib/tracker-icons.test.ts
index 1898b79..a293461 100644
--- a/src/lib/tracker-icons.test.ts
+++ b/src/lib/tracker-icons.test.ts
@@ -1,4 +1,6 @@
import { describe, expect, it } from 'vitest';
+import { existsSync } from 'node:fs';
+import { gameLogos } from './game-logos';
import { trackerIcons } from './tracker-icons';
import { gameTrackers, spottingTrackers, visibleTrackers } from './projects';
@@ -16,6 +18,17 @@ describe('tracker icons', () => {
expect(new Set(keys.map((k) => trackerIcons[k].to)).size).toBe(keys.length);
});
+ it('gives every game a self-hosted official logo', () => {
+ const keys = gameTrackers.map((g) => g.icon ?? g.name);
+ for (const key of keys) {
+ const logo = gameLogos[key];
+ expect(logo, key).toBeDefined();
+ expect(logo.src, key).toMatch(/^game-logos\/icons\/[a-z0-9-]+\.(svg|png)$/);
+ expect(existsSync(`static/${logo.src}`), logo.src).toBe(true);
+ }
+ expect(Object.keys(gameLogos).sort()).toEqual([...keys].sort());
+ });
+
it('links every game to its own page under /trackers/games', () => {
const slugs = gameTrackers.map((g) => g.href.replace('/trackers/games/', ''));
for (const slug of slugs) expect(slug).toMatch(/^[a-z0-9-]+$/);
diff --git a/src/routes/projects/game-tracker/credits/+page.svelte b/src/routes/projects/game-tracker/credits/+page.svelte
index d1766a3..f0bdd1e 100644
--- a/src/routes/projects/game-tracker/credits/+page.svelte
+++ b/src/routes/projects/game-tracker/credits/+page.svelte
@@ -1,5 +1,8 @@
@@ -16,6 +19,23 @@
stores WebP copies for consistent, reliable display. Map imagery is shared across games in the
same region; later-region landmark positions are approximate.
+ Game logos
+
+ {#each gameTrackers as game (game.href)}
+ {@const logo = gameLogos[game.icon ?? game.name]}
+ {#if logo}
+ -
+
+
+
+ {/if}
+ {/each}
+
Regional maps
{#each data.maps as map (map.name)}-
diff --git a/static/game-logos/icons/bo2.png b/static/game-logos/icons/bo2.png
new file mode 100644
index 0000000..a9b9ad1
Binary files /dev/null and b/static/game-logos/icons/bo2.png differ
diff --git a/static/game-logos/icons/cs2.png b/static/game-logos/icons/cs2.png
new file mode 100644
index 0000000..0e6caa9
Binary files /dev/null and b/static/game-logos/icons/cs2.png differ
diff --git a/static/game-logos/icons/cubing.svg b/static/game-logos/icons/cubing.svg
new file mode 100644
index 0000000..c559d85
--- /dev/null
+++ b/static/game-logos/icons/cubing.svg
@@ -0,0 +1,29 @@
+
+
+
\ No newline at end of file
diff --git a/static/game-logos/icons/factorio.png b/static/game-logos/icons/factorio.png
new file mode 100644
index 0000000..a96236c
Binary files /dev/null and b/static/game-logos/icons/factorio.png differ
diff --git a/static/game-logos/icons/ksp.png b/static/game-logos/icons/ksp.png
new file mode 100644
index 0000000..3583634
Binary files /dev/null and b/static/game-logos/icons/ksp.png differ
diff --git a/static/game-logos/icons/lol.svg b/static/game-logos/icons/lol.svg
new file mode 100644
index 0000000..cbf87e3
--- /dev/null
+++ b/static/game-logos/icons/lol.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/game-logos/icons/minecraft.svg b/static/game-logos/icons/minecraft.svg
new file mode 100644
index 0000000..728b36e
--- /dev/null
+++ b/static/game-logos/icons/minecraft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/game-logos/icons/osu.svg b/static/game-logos/icons/osu.svg
new file mode 100644
index 0000000..03055b8
--- /dev/null
+++ b/static/game-logos/icons/osu.svg
@@ -0,0 +1 @@
+
diff --git a/static/game-logos/icons/overwatch.svg b/static/game-logos/icons/overwatch.svg
new file mode 100644
index 0000000..cea308b
--- /dev/null
+++ b/static/game-logos/icons/overwatch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/game-logos/icons/pokemon.svg b/static/game-logos/icons/pokemon.svg
new file mode 100644
index 0000000..76cbef4
--- /dev/null
+++ b/static/game-logos/icons/pokemon.svg
@@ -0,0 +1,31 @@
+
+
+
diff --git a/static/game-logos/icons/pubg.png b/static/game-logos/icons/pubg.png
new file mode 100644
index 0000000..1aed927
Binary files /dev/null and b/static/game-logos/icons/pubg.png differ
diff --git a/static/game-logos/icons/r6.png b/static/game-logos/icons/r6.png
new file mode 100644
index 0000000..424bac5
Binary files /dev/null and b/static/game-logos/icons/r6.png differ
diff --git a/static/game-logos/icons/smash.svg b/static/game-logos/icons/smash.svg
new file mode 100644
index 0000000..b0a8adc
--- /dev/null
+++ b/static/game-logos/icons/smash.svg
@@ -0,0 +1 @@
+
diff --git a/static/game-logos/icons/sot.png b/static/game-logos/icons/sot.png
new file mode 100644
index 0000000..4352edc
Binary files /dev/null and b/static/game-logos/icons/sot.png differ
diff --git a/static/game-logos/icons/tarkov.png b/static/game-logos/icons/tarkov.png
new file mode 100644
index 0000000..ff3677e
Binary files /dev/null and b/static/game-logos/icons/tarkov.png differ
diff --git a/static/game-logos/icons/valorant.svg b/static/game-logos/icons/valorant.svg
new file mode 100644
index 0000000..15a8307
--- /dev/null
+++ b/static/game-logos/icons/valorant.svg
@@ -0,0 +1 @@
+
\ No newline at end of file