Bilder einfach zweitem Bildschirm präsentieren
Man hat zu viele bunte Bilder gesammelt, um sie alle für den Spieltisch auszudrucken. PowerPoint wäre zwar eine Option, aber entweder bereitet man im Vorfeld Folien vor, oder man muss die Bilder während der Session live auf die richtige Größe ziehen. Um das auf die Schnelle zu lösen, habe ich mir von Gemini eine kleine HTML-Datei erzeugen lassen: den GM Image Presenter.
Die Bedienung ist denkbar simpel: Man zieht ein Bild einfach per Drag & Drop ins Fenster, und die Bilder werden automatisch bildschirmfüllend angeordnet. Es gibt zudem ein zweites Fenster, das man auf einen zweiten Monitor ziehen kann und das exakt dasselbe anzeigt wie das Hauptfenster. Um ein Bild wieder zu entfernen, klickt man im Hauptfenster einfach darauf.
Hier geht es direkt zum Tool: GM Image Presenter
Wie funktioniert das Ganze im Detail?
Das Prinzip ist auf maximalen Komfort für dich als Spielleiter (GM) ausgelegt:
- Der Meister-Bildschirm (Steuerung): Du hast ein Hauptfenster auf deinem Laptop oder Monitor, das nur du siehst. Hier ziehst du per Drag & Drop einfach die Bilder rein, die du brauchst – seien es grimmige NSCs im feinen „Painterly Realism“-Stil, Wappen oder Grundrisse. Das Tool ordnet die Bilder automatisch und übersichtlich in einem Grid an.
- Der Spieler-Bildschirm (Präsentation): Mit einem Klick auf „Open 2nd Window“ öffnet sich ein zweites Fenster. Dieses schiebst du auf den Fernseher, den Beamer oder den Monitor, der zu den Spielern gerichtet ist.
- Echtzeit-Synchronisation: Alles, was du im Hauptfenster tust, wird live auf dem Spieler-Bildschirm gespiegelt. Klickst du ein Bild im Hauptfenster an, verschwindet es, und das Layout passt sich für die restlichen Bilder automatisch neu an. Das Spieler-Fenster ist dabei komplett „clean“ (ohne Menüs oder Mausanzeigen) und kann mit einem Klick in den Vollbildmodus versetzt werden.
100% Offline
Das Beste an dem Tool: Es ist keine Software, die du aufwendig installieren musst, und auch kein Cloud-Dienst. Es ist einfach nur eine einzige HTML-Datei.
So holst du dir das Tool:
Du kannst das Tool direkt im Browser testen oder es dir für deine nächste Runde lokal auf die Festplatte ziehen:
- Öffne diesen Link: GM Image Presenter (GitHub Pages)
- Wenn du es offline nutzen willst, drücke auf der Seite einfach
Strg + S(oder Rechtsklick -> „Speichern unter…“). So kannst du die Datei abspeichern und vor jeder Session einfach per Doppelklick in deinem Browser öffnen.
Alternativ kannst du den code unter kopieren:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>GM Image Presenter</title> <style id="main-styles"> body, html { margin: 0; padding: 0; width: 100vw; height: 100vh; background-color: #121212; overflow: hidden; /* Prevents any scrolling */ font-family: sans-serif; } #container { display: grid; width: 100%; height: 100%; gap: 10px; padding: 10px; box-sizing: border-box; } .grid-image { width: 100%; height: 100%; object-fit: contain; /* Keeps aspect ratio intact */ /* Forces images to shrink to fit the grid cells */ min-width: 0; min-height: 0; cursor: pointer; transition: transform 0.1s ease; } .grid-image:hover { transform: scale(1.02); } /* Prevent clicking/hovering in the mirror window */ .mirror .grid-image { cursor: default; transform: none !important; } #ui-layer { position: absolute; top: 15px; left: 15px; z-index: 100; } button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; box-shadow: 0 4px 6px rgba(0,0,0,0.3); transition: background-color 0.2s; } button:hover { background-color: #0056b3; } #drag-hint { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #555; font-size: 24px; pointer-events: none; } </style></head><body> <div id="ui-layer"> <button onclick="openSecondWindow()">Open 2nd Window</button> </div> <div id="drag-hint">Drag & Drop Images Here</div> <div id="container"></div> <script> const container = document.getElementById('container'); const dragHint = document.getElementById('drag-hint'); let secondWindow = null; // --- Layout & Sync Logic --- function updateLayout() { const count = container.children.length; // Hide hint if images exist dragHint.style.display = count > 0 ? 'none' : 'block'; if (count > 0) { // Calculate optimal rows and columns to share space evenly const cols = Math.ceil(Math.sqrt(count)); const rows = Math.ceil(count / cols); container.style.gridTemplateColumns = `repeat(${cols}, 1fr)`; container.style.gridTemplateRows = `repeat(${rows}, 1fr)`; } syncSecondWindow(); } function syncSecondWindow() { if (secondWindow && !secondWindow.closed) { const mirrorContainer = secondWindow.document.getElementById('container'); if (mirrorContainer) { // Copy the exact HTML into the second window's container mirrorContainer.innerHTML = container.innerHTML; // Apply the exact same grid layout styles mirrorContainer.style.gridTemplateColumns = container.style.gridTemplateColumns; mirrorContainer.style.gridTemplateRows = container.style.gridTemplateRows; } } } function openSecondWindow() { if (secondWindow && !secondWindow.closed) { secondWindow.focus(); return; } // Open a blank window secondWindow = window.open('', 'ImageGridMirror', 'width=800,height=600'); // Grab the CSS from this main file to style the pop-up const styles = document.getElementById('main-styles').innerHTML; // Write the HTML skeleton, CSS, and Fullscreen Logic to the new window // Note: The script tag closing bracket is escaped so it doesn't break the main file secondWindow.document.write(` <!DOCTYPE html> <html> <head> <title>Image Grid (Display)</title> <style> ${styles} /* Styles specifically for the second window's fullscreen button */ #mirror-ui { position: absolute; bottom: 20px; right: 20px; opacity: 0; transition: opacity 0.3s ease; z-index: 1000; } /* Reveal the button when mouse is over the window body */ body:hover #mirror-ui { opacity: 1; } </style> </head> <body> <div id="mirror-ui"> <button onclick="toggleFullScreen()">Toggle Fullscreen</button> </div> <div id="container" class="mirror"></div> <script> function toggleFullScreen() { if (!document.fullscreenElement) { document.documentElement.requestFullscreen().catch(err => { alert(\`Error attempting to enable fullscreen: \${err.message}\`); }); } else { if (document.exitFullscreen) { document.exitFullscreen(); } } } <\/script> </body> </html> `); secondWindow.document.close(); // Run an initial sync setTimeout(syncSecondWindow, 100); } // --- Drag & Drop Logic --- window.addEventListener('dragover', (e) => { e.preventDefault(); // Necessary to allow dropping e.dataTransfer.dropEffect = 'copy'; }); window.addEventListener('drop', (e) => { e.preventDefault(); const files = e.dataTransfer.files; for (let i = 0; i < files.length; i++) { const file = files[i]; // Only process image files if (!file.type.startsWith('image/')) continue; const reader = new FileReader(); reader.onload = (event) => { const img = document.createElement('img'); img.src = event.target.result; img.className = 'grid-image'; // Click to remove functionality img.onclick = function() { this.remove(); updateLayout(); }; container.appendChild(img); updateLayout(); }; // Read local file into base64 data URL reader.readAsDataURL(file); } }); </script></body></html>







