Compare commits

19 Commits
Author SHA1 Message Date
stefankoelle b34262e11f Add chat-archive description 2026-09-17 16:45:17 +02:00
stefankoelle 17c01086b7 Update repos and CV from prebuild 2026-09-17 16:21:56 +02:00
stefankoelle 5e870d1466 Revert Kölle to Koelle in meta/title/alt tags for SEO, keep Kölle in visible text 2026-09-14 12:15:26 +02:00
stefankoelle 8d00f6d1fc Retro: Add 28k8 BBS Archive card to Overview section 2026-09-13 23:44:49 +02:00
stefankoelle 51f8e0264d TCM: Fix menu intro text 2026-09-13 23:33:43 +02:00
stefankoelle 0fa880683a TCM: Fix text formulations, grammar and consistency 2026-09-13 23:30:38 +02:00
stefankoelle bda845dd8e TCM: Enable starfield on mobile 2026-09-13 23:27:20 +02:00
stefankoelle 6f3d6a1661 TCM: Add VT323/Press Start 2P fonts, rebuild tables, remove code tags, fix h1 size 2026-09-13 23:23:23 +02:00
stefankoelle 5f4b008545 Self-host Lobster font, remove Google Fonts calls from shared and stefankoelle 2026-09-13 23:15:49 +02:00
stefankoelle 5c1c6ed697 Self-host VT323 and Press Start 2P fonts, remove Google Fonts calls 2026-09-13 23:05:08 +02:00
stefankoelle 96a8a15e2b Add video play overlay, fix text formulations, improve readability 2026-09-13 23:00:24 +02:00
stefankoelle 05cafca9fc Stack table vertically on mobile for better readability 2026-09-13 22:55:44 +02:00
stefankoelle a71ca2f536 Add VT323/Press Start 2P fonts, enable animations on mobile, fix mobile layout 2026-09-13 22:52:59 +02:00
stefankoelle acd50bfdd8 Add copper bars background, fix mobile logo display, layout improvements 2026-09-13 22:38:24 +02:00
stefankoelle b0943dd878 Add palette rotation animation to PHOBIA logo, improve layout and responsive design 2026-09-13 22:27:59 +02:00
stefankoelle b817c54332 Add parallax starfield background to TCM archive page 2026-09-13 21:55:42 +02:00
stefankoelle 7d57de34d0 Add h1 heading to TCM archive page 2026-09-13 21:50:33 +02:00
stefankoelle f76521c02d Fix mega-st-high gallery captions: original Atari keyboard in image 2, Cherry PS/2 + Logitech in image 6 2026-09-13 18:47:43 +02:00
stefankoelle 46b9c1b64d Fix mega-st-high gallery captions to match actual image content, add skoelle sync note to AGENTS.md 2026-09-13 18:44:32 +02:00
23 changed files with 395 additions and 113 deletions
+4
View File
@@ -161,6 +161,10 @@ CSS-Dateien werden als Passthrough kopiert (kein Hash im Dateinamen). Bei CSS-Ae
Bei jeder CSS-Anpassung `?v=N` um 1 erhoehen, sonst cached der Browser die alte Datei.
### skoelle GitHub-Repo im Auge behalten
Bei Aenderungen an stefankoelle.de (Inhalte, Technologien, URLs) pruefen, ob die Datei `../skoelle/README.md` ebenfalls aktualisiert werden muss. Das README ist die Kurzform des Lebenslaufs und soll mit stefankoelle.de synchron bleiben.
## CI/CD
### IONOS SFTP (www.moonweb.org)
+34 -1
View File
@@ -1,4 +1,21 @@
[
{
"title": "Chat Archive",
"emoji": "💬",
"category": "code",
"subcategory": "Dev Tools",
"status": "active",
"stack": [
"Python",
"FastAPI",
"SQLAlchemy",
"Tauri",
"Rust"
],
"repo": "chat-archive",
"repo_url": "https://github.com/skoelle/chat-archive",
"summary": "Import Instagram & Facebook (incl. E2EE) chat takeouts into a searchable MySQL database with a REST API for analysis"
},
{
"title": "PeopleCostCounter",
"emoji": "💰",
@@ -14,6 +31,22 @@
"repo_url": "https://github.com/skoelle/PeopleCostCounter",
"summary": "A vanilla JS single-page tool that shows in real time how much a meeting is costing, based on team size and average salary, with English and German UIs."
},
{
"title": "Smeagol WYSIWYG",
"emoji": "🧙",
"category": "code",
"subcategory": "Dev Tools",
"description": "Personal wiki with WYSIWYG editor for Markdown files",
"status": "active",
"stack": [
"Go",
"Milkdown",
"Markdown"
],
"repo": "smeagol-wysiwyg",
"repo_url": "https://github.com/skoelle/smeagol-wysiwyg",
"summary": "A personal wiki with a real WYSIWYG editor. Single Go binary, Markdown files on disk, instant save, live reload, full-text search."
},
{
"title": "kctl-tui",
"emoji": "🐳",
@@ -259,7 +292,7 @@
],
"repo": "moonweb-site",
"repo_url": "https://github.com/skoelle/moonweb-site",
"summary": "Monorepo for moonweb.org - my personal homelab hub. Six static sites (Eleventy) covering infrastructure, smart home, code, and retro hardware. Deployed to Cloudflare Pages or IONOS."
"summary": "Monorepo for moonweb.org - my personal homelab hub. Six static sites (Eleventy) covering infrastructure, smart home, code, and retro hardware. Deployed to IONOS."
},
{
"title": "www.moonweb.org",
+1
View File
@@ -56,6 +56,7 @@ module.exports = function (eleventyConfig) {
// Root-level shared assets
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/fonts/lobster-latin.woff2": "fonts/lobster-latin.woff2" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/hub.svg": "favicon.svg" });
eleventyConfig.addPassthroughCopy({ "hub/robots.txt": "robots.txt" });
+3 -3
View File
@@ -1,9 +1,9 @@
---
title: "Stefan Kölle, Homelab, Smart Home, Code & Retro Hardware | moonweb"
title: "Stefan Koelle, Homelab, Smart Home, Code & Retro Hardware | moonweb"
section: "hub"
tags: "hub"
permalink: /index.html
description: "Stefan Kölle, Senior Software Developer & Architect in Munich. Homelab projects, smart home automation, open-source code, and retro hardware."
description: "Stefan Koelle, Senior Software Developer & Architect in Munich. Homelab projects, smart home automation, open-source code, and retro hardware."
layout: base.njk
ogImage: "/stefan-koelle-foto.jpg"
sections:
@@ -57,7 +57,7 @@ sections:
<p>I'm a Senior Software Developer &amp; Architect based in Munich with 20+ years in the trenches, from Turbo Pascal on a real 486 back in the day to .NET, Kubernetes, and AI-driven development today. By day I build microservices and cloud architecture, by night I dive into smart home automation, restore retro computers, and automate things that don't necessarily need automating.</p>
<p>Everything here runs on a homelab that has been evolving since the mid-90s, from multiple 486s and Pentiums back then to today's Proxmox cluster. Have a look around.</p>
</div>
<img class="intro-photo" src="/stefan-koelle-foto.jpg" alt="Stefan Kölle" width="280" height="280">
<img class="intro-photo" src="/stefan-koelle-foto.jpg" alt="Stefan Koelle" width="280" height="280">
</div>
{% include "card-grid.njk" %}
Binary file not shown.
Binary file not shown.
+214 -36
View File
@@ -7,22 +7,40 @@
<meta name="description" content="Explore the PHOBIA DemoGroup Archive, showcasing the 1994 PC intros, including the notable Phobia Welcome Intro. Relive the early days of PC intros with source code and more.">
<link rel="canonical" href="https://www.moonweb.org/phobia/" />
<style>
body {
@font-face {
font-family: 'VT323';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('fonts/vt323-latin.woff2') format('woff2');
}
@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('fonts/pressstart2p-latin.woff2') format('woff2');
}
body {
background-color: #1a1a1a;
margin: 0;
color: #f0f0f0;
font-family: 'Arial', sans-serif;
font-family: 'VT323', monospace;
line-height: 1.6;
font-size: 20px;
overflow-x: hidden;
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}
.header {
display: flex;
align-items: center;
justify-content: center;
background-color: #000071;
padding-top:5px;
padding-left: 20px;
padding: 10px 0;
}
.header img {
margin-right: 20px;
.header img, .header canvas {
height: 120px;
margin-right: 0;
}
.header a {
margin: 0 15px;
@@ -36,7 +54,9 @@
.content {
display: flex;
flex-wrap: wrap;
padding: 40px;
padding: 20px 15px;
max-width: 1000px;
margin: 0 auto;
}
.program {
display: flex;
@@ -52,15 +72,40 @@
height: auto;
border-radius: 10px;
}
.video-thumb {
position: relative;
cursor: pointer;
}
.play-button {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 0;
height: 0;
border-left: 35px solid rgba(255,255,255,0.9);
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
filter: drop-shadow(0 0 6px rgba(0,0,0,0.6));
transition: opacity 0.2s;
}
.video-thumb:hover .play-button {
opacity: 0.7;
}
.program-description {
margin-top: 10px;
font-size: 1.1em;
line-height: 1.4;
}
.program-description p {
margin: 0.3em 0;
}
.program-info {
flex: 2 1 300px;
}
.program-info h2 {
font-size: 2em;
font-size: 1em;
margin-top: 0;
margin-bottom: 10px;
}
.footer {
@@ -70,18 +115,23 @@
background-color: #333;
margin-top: 20px;
}
@media (max-width: 768px) {
h1, h2 {
font-family: 'Press Start 2P', cursive;
}
@media (max-width: 600px) {
h1 {
font-size: 1.7em;
}
.program-info h2 {
font-size: 1.5em;
font-size: 1.2em;
margin-bottom: 10px;
}
.header img {
.header img, .header canvas {
width: 100%;
height: auto;
}
.content {
padding: 15px 10px;
flex-direction: column;
align-items: left;
}
@@ -96,9 +146,26 @@
.program-info {
text-align: left;
}
.content table {
width: 100%;
}
.content table tr {
display: flex;
flex-direction: column;
margin-bottom: 0.8em;
}
.content table td {
padding-right: 0;
}
.content table td:first-child {
margin-bottom: 0.2em;
}
}
th {
th, td {
text-align:left;
vertical-align: top;
padding-right: 20px;
overflow-wrap: break-word;
}
a {
color:#00A6FF;
@@ -150,22 +217,26 @@
<body>
<div class="header">
<a href="index.html">
<img src="phobialogo.png" alt="PHOBIA">
<canvas id="phobialogo" style="margin-right:20px;image-rendering:pixelated;"></canvas>
<img id="phobialogo-fallback" src="phobialogo.png" alt="PHOBIA" style="display:none;">
</a>
</div>
<div class="content">
<div class="welcome">
<h1>Welcome to the PHOBIA DemoGroup Archive</h1>
<h1>PHOBIA DemoGroup Archive</h1>
<p>This site showcases the releases from my DemoGroup, PHOBIA. Although PHOBIA existed only briefly on the PC in 1994, it made a notable impact with its focus on PC intros. The highlight of our work was the major release, the Phobia Welcome Intro.</p>
<p>This site showcases the releases from my DemoGroup, PHOBIA. Although PHOBIA existed only briefly in 1994, it made a notable impact with its focus on PC intros. The highlight of our work was the Phobia Welcome Intro.</p>
<p>Feel free to explore and enjoy the nostalgia of the early days of PC intros.</p>
<p>Best regards, Stefan Koelle</p>
<p style="margin-bottom: 1.5em;">Best regards, Stefan Koelle</p>
</div>
<div class="program">
<div class="program-image">
<img src="welcomeintro.png" alt="Program 1 Image" onclick="openLightbox()">
<div class="video-thumb" onclick="openLightbox()">
<img src="welcomeintro.png" alt="Program 1 Image">
<div class="play-button"></div>
</div>
<div class="program-description">
<p><b>Phobia Welcome Intro</b></p>
<p><a href="#" onclick="openLightbox()">YouTube Video of the Intro</a></p>
@@ -174,55 +245,51 @@
</div>
<div class="program-info">
<h2>Phobia Welcome Intro</h2>
<p>The Phobia Welcome Intro was programmed in 1994 using Turbo Pascal, with inline assembler for the graphics routines. The idea for this intro was inspired by an Amiga demo, though the specific name of the demo has been forgotten. All graphics and programming were done by Stefan Koelle on a 486 DX2-66 DOS PC.</p>
<p>This intro utilized an early version of the X-LIB unit, specifically the TextGraf.pas, which was also developed by Stefan Koelle. The source code for the intro is available on GitHub. The primary technique used in this intro to create the illusion of movement was through color palette changes, rather than altering the pixels on the screen.</p>
<p>The music for the intro was borrowed from an old Amiga demo and played using a public Mod-Player. The font used in the intro was custom-created and compiled into the executable as an object, along with the music and graphics.</p>
<p>The Phobia Welcome Intro was programmed in 1994 using Turbo Pascal, with inline assembler for the graphics routines. This intro was inspired by an Amiga demo, though the specific name has been forgotten. All graphics and programming were done by Stefan Koelle on a 486 DX2-66 DOS PC.</p>
<p>This intro utilized an early version of the X-LIB unit, specifically the TextGraf.pas, which was also developed by Stefan Koelle. The source code for the intro is available on GitHub. The primary technique used to create the illusion of movement was color palette changes, rather than altering the pixels on the screen.</p>
<p>The music for the intro was borrowed from an old Amiga demo and played using a public Mod-Player. The font used in the intro was custom and compiled into the executable as an object, along with the music and graphics.</p>
<h2>Key Details:</h2>
<h2>Facts</h2>
<table>
<tr>
<th>Feature</th>
<th>Description</th>
</tr>
<tr>
<td>Programming Language</td>
<td><b>Language</b></td>
<td>Turbo Pascal with inline assembler</td>
</tr>
<tr>
<td>Year</td>
<td><b>Year</b></td>
<td>1994</td>
</tr>
<tr>
<td>Developer</td>
<td><b>Developer</b></td>
<td>Stefan Koelle</td>
</tr>
<tr>
<td>Development Machine</td>
<td><b>Development Machine</b></td>
<td>486 DX2-66 DOS PC</td>
</tr>
<tr>
<td>Graphics Technique</td>
<td><b>Graphics Technique</b></td>
<td>Color palette changes</td>
</tr>
<tr>
<td>Music Source</td>
<td><b>Music Source</b></td>
<td>Old Amiga demo</td>
</tr>
<tr>
<td>Music Player</td>
<td><b>Music Player</b></td>
<td>Public Mod-Player</td>
</tr>
<tr>
<td>Custom Font</td>
<td><b>Custom Font</b></td>
<td>Compiled into the executable as an object</td>
</tr>
<tr>
<td>Graphics Library</td>
<td><b>Graphics Library</b></td>
<td>Early version of X-LIB unit (TextGraf.pas)</td>
</tr>
<tr>
<td>Source Code</td>
<td>Available on GitHub <a href="https://github.com/skoelle/dos-phobia-welcome-intro" target="_blank">https://github.com/skoelle/dos-phobia-welcome-intro</a></td>
<td><b>Source Code</b></td>
<td>Available on GitHub<br><a href="https://github.com/skoelle/dos-phobia-welcome-intro" target="_blank">https://github.com/skoelle/dos-phobia-welcome-intro</a></td>
</tr>
</table>
</div>
@@ -255,6 +322,7 @@
</div>
</div>-->
</div>
<canvas id="copperbars" style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;pointer-events:none;"></canvas>
<div class="footer">
PHOBIA website - initially created November 1999 - code from 1994 - Stefan Koelle<br>
Part of <a href="/">moonweb.org</a> · <a href="/retro/">Retro Corner</a> · <a href="/impressum/">Legal Notice</a>
@@ -265,5 +333,115 @@
<iframe id="video" src="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
<script>
(function() {
var canvas = document.getElementById('phobialogo');
var fallback = document.getElementById('phobialogo-fallback');
var ctx = canvas.getContext('2d');
var img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
fallback.style.display = 'none';
canvas.style.display = 'inline';
var origData = null;
try {
ctx.drawImage(img, 0, 0);
origData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch(e) {
fallback.style.display = 'inline';
canvas.style.display = 'none';
return;
}
var pixels = origData.data;
var blueMap = {};
for (var i = 0; i < pixels.length; i += 4) {
var r = pixels[i], g = pixels[i+1], b = pixels[i+2];
if (b > 30 && b > r * 1.3 && b > g * 1.2) {
var key = r + ',' + g + ',' + b;
if (!blueMap[key]) blueMap[key] = { r: r, g: g, b: b, count: 0 };
blueMap[key].count++;
}
}
var palette = [];
for (var k in blueMap) {
if (blueMap[k].count >= 500) palette.push(blueMap[k]);
}
palette.sort(function(a, b) {
var la = a.r * 0.299 + a.g * 0.587 + a.b * 0.114;
var lb = b.r * 0.299 + b.g * 0.587 + b.b * 0.114;
return la - lb;
});
var origColors = palette.map(function(c) { return { r: c.r, g: c.g, b: c.b }; });
var rotColors = origColors.slice();
var offset = 0;
function draw() {
var out = ctx.createImageData(canvas.width, canvas.height);
out.data.set(pixels);
var d = out.data;
for (var i = 0; i < palette.length; i++) {
rotColors[i] = origColors[(i + offset) % palette.length];
}
var lookup = {};
for (var i = 0; i < palette.length; i++) {
var oc = origColors[i];
var rc = rotColors[i];
lookup[oc.r + ',' + oc.g + ',' + oc.b] = rc;
}
for (var i = 0; i < d.length; i += 4) {
var key = d[i] + ',' + d[i+1] + ',' + d[i+2];
if (lookup[key]) {
d[i] = lookup[key].r;
d[i+1] = lookup[key].g;
d[i+2] = lookup[key].b;
}
}
ctx.putImageData(out, 0, 0);
offset = (offset + 1) % palette.length;
setTimeout(function() { requestAnimationFrame(draw); }, 120);
}
draw();
};
img.onerror = function() {
fallback.style.display = 'inline';
canvas.style.display = 'none';
};
img.src = 'phobialogo.png';
})();
</script>
<script>
(function() {
var canvas = document.getElementById('copperbars');
var ctx = canvas.getContext('2d');
var w, h;
function resize() { w = canvas.width = window.innerWidth; h = canvas.height = window.innerHeight; }
resize();
window.addEventListener('resize', resize);
var bars = [
{ baseY: 0.45, amplitude: 0.18, speed: 0.7, r: 255, g: 199, b: 0, barH: 50 },
{ baseY: 0.55, amplitude: 0.15, speed: 1.1, r: 239, g: 0, b: 0, barH: 50 },
{ baseY: 0.52, amplitude: 0.20, speed: 0.9, r: 4, g: 203, b: 0, barH: 50 },
];
function draw(t) {
ctx.clearRect(0, 0, w, h);
for (var i = 0; i < bars.length; i++) {
var bar = bars[i];
var y = h * bar.baseY + Math.sin(t * 0.001 * bar.speed) * h * bar.amplitude;
var halfH = bar.barH / 2;
for (var row = -halfH; row <= halfH; row++) {
var rel = row / halfH;
var alpha = 1 - rel * rel;
var py = Math.round(y + row);
if (py < 0 || py >= h) continue;
ctx.fillStyle = 'rgba(' + bar.r + ',' + bar.g + ',' + bar.b + ',' + alpha.toFixed(3) + ')';
ctx.fillRect(0, py, w, 1);
}
}
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
})();
</script>
</body>
</html>
+3
View File
@@ -123,6 +123,9 @@ sections:
- title: "📅 Computer Timeline"
summary: "From Atari ST and Amiga 500 to DOS 486, the personal computing story behind the retro corner."
href: "/retro/computer-timeline/"
- title: "💾 28k8 BBS Archive"
summary: "tHE tEMPLE BBS Mailbox and 90s scene releases archive from the 28k8 era."
href: "https://28k8.moonweb.org"
---
<h1>Retro Computing</h1>
+9 -9
View File
@@ -39,22 +39,22 @@ parent: "/retro/"
<h2>Gallery</h2>
<img src="high-1.jpg" alt="The Mega ST 4 High machine" width="640" height="640" loading="lazy">
<p class="caption">The Mega ST 4 "High" workstation.</p>
<p class="caption">The NeoDesk desktop showing 4 MB ST RAM and 8 MB AltRAM from the MonSTerBoard.</p>
<img src="high-2.jpg" alt="Mega ST 4 High internal view" width="640" height="640" loading="lazy">
<p class="caption">Inside the "High" machine.</p>
<p class="caption">The workstation running GEMBench, with the original Atari keyboard and mouse.</p>
<img src="high-3.jpg" alt="Mega ST 4 High detail" width="640" height="853" loading="lazy">
<p class="caption">A detail of the "High" setup.</p>
<p class="caption">GEMBench 6.31 results comparing the ST reference to the High machine at 1024x768 with 256 colors.</p>
<img src="high-4.jpg" alt="Mega ST 4 High expansion" width="640" height="480" loading="lazy">
<p class="caption">Expansion boards in the "High" machine.</p>
<p class="caption">The complete setup in daylight, showing the GEM desktop on the EIZO FlexScan L365.</p>
<img src="high-5.jpg" alt="Mega ST 4 High close-up" width="640" height="640" loading="lazy">
<p class="caption">Close-up of the "High" machine.</p>
<p class="caption">Geneva multitasking: qed, Sound Player, World Clock, and Memory usage running side by side.</p>
<img src="high-6.jpg" alt="Mega ST 4 High running" width="640" height="480" loading="lazy">
<p class="caption">The "High" machine in operation.</p>
<p class="caption">A side view of the setup, showing the Cherry PS/2 keyboard, Logitech mouse via the Eiffel adapter, and the floppy drive.</p>
<img src="high-7.jpg" alt="Mega ST 4 High setup" width="640" height="480" loading="lazy">
<p class="caption">The "High" workstation setup.</p>
<p class="caption">Geneva running Vision 3.5a and qed in parallel, with green ambient light behind the EIZO.</p>
<img src="high-8.jpg" alt="Mega ST 4 High board" width="640" height="640" loading="lazy">
<p class="caption">Board detail of the "High" machine.</p>
<p class="caption">The NeoDesk Applications menu with Geneva Manager, JAnE, and Vision 3.5a, alongside the system spec file.</p>
<img src="high-9.jpg" alt="Mega ST 4 High final" width="640" height="480" loading="lazy">
<p class="caption">The finished "High" machine.</p>
<p class="caption">The full workstation at night, driven by the MonSTerBoard, ET4000, and TOS 2.06.</p>
</div>
+3 -6
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}{% if 'moonweb' not in title and 'stefankoelle' not in title %} · moonweb.org{% endif %}</title>
<meta name="description" content="{{ description }}">
<meta name="author" content="Stefan Kölle">
<meta name="author" content="Stefan Koelle">
<meta name="theme-color" content="{% if section == 'hub' %}#3b6ea5{% elif section == 'infra' %}#99333A{% elif section == 'smarthome' %}#1f8a8a{% elif section == 'code' %}#3E5098{% elif section == 'retro' %}#8a6d3b{% else %}#3b6ea5{% endif %}">
<link rel="canonical" href="{{ site.url }}{{ page.url }}">
<meta property="og:title" content="{{ title }}{% if 'moonweb' not in title and 'stefankoelle' not in title %} · moonweb.org{% endif %}">
@@ -18,9 +18,6 @@
<meta name="twitter:title" content="{{ title }}{% if 'moonweb' not in title and 'stefankoelle' not in title %} · moonweb.org{% endif %}">
<meta name="twitter:description" content="{{ description }}">
{% if ogImage %}<meta name="twitter:image" content="{{ site.url }}{{ ogImage }}">{% endif %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet">
<link rel="icon" href="{% if section and section != 'hub' %}/{{ section }}/favicon.svg{% else %}/favicon.svg{% endif %}">
<link rel="stylesheet" href="/shared-base.css">
<style>:root { --accent: {% if section == 'infra' %}#99333A{% elif section == 'smarthome' %}#1f8a8a{% elif section == 'code' %}#3E5098{% elif section == 'retro' %}#8a6d3b{% else %}#3b6ea5{% endif %}; }</style>
@@ -36,7 +33,7 @@
{% else %}
"name": "{{ title | safe }}{% if 'moonweb' not in title and 'stefankoelle' not in title %} · moonweb.org{% endif %}",
"url": "{{ site.url }}",
"author": { "@type": "Person", "name": "Stefan Kölle" }
"author": { "@type": "Person", "name": "Stefan Koelle" }
{% endif %}
}
</script>
@@ -45,7 +42,7 @@
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Stefan Kölle",
"name": "Stefan Koelle",
"url": "https://www.moonweb.org",
"image": "https://www.moonweb.org/stefan-koelle-foto.jpg",
"jobTitle": "Senior Software Developer & Architect",
+8
View File
@@ -10,6 +10,14 @@
* { box-sizing: border-box; }
@font-face {
font-family: 'Lobster';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('fonts/lobster-latin.woff2') format('woff2');
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
Binary file not shown.
-3
View File
@@ -14,9 +14,6 @@
<meta name="twitter:title" content="{{ title }} · {{ siteTitle | default("moonweb.org") }}">
<meta name="twitter:description" content="{{ description }}">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared-base.css">
<link rel="stylesheet" href="/theme.css">
</head>
+4 -4
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="theme-color" content="#ffffff">
<title>{% if title %}{{ title }}{% else %}Stefan Kölle{% endif %}</title>
<title>{% if title %}{{ title }}{% else %}Stefan Koelle{% endif %}</title>
<meta name="description" content="{{ description }}">
<link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-icon-60x60.png">
@@ -25,7 +25,7 @@
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" href="/assets/ms-icon-144x144.png">
<link rel="canonical" href="{{ site.url }}{{ page.url }}">
<meta name="author" content="Stefan Kölle">
<meta name="author" content="Stefan Koelle">
<meta property="og:title" content="{{ title }}">
<meta property="og:description" content="{{ description }}">
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
@@ -39,7 +39,7 @@
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Stefan Kölle",
"name": "Stefan Koelle",
"url": "{{ site.url }}",
"jobTitle": "Software Developer",
"sameAs": [
@@ -84,7 +84,7 @@
</div>
<div id="head">
<div id="foto"><img src="/assets/stefan-koelle-foto.jpg" alt="Photo of Stefan Kölle" style="width: 100%;"></div>
<div id="foto"><img src="/assets/stefan-koelle-foto.jpg" alt="Photo of Stefan Koelle" style="width: 100%;"></div>
</div>
<div id="background-image"></div>
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "Stefan Kölle",
"name": "Stefan Koelle",
"short_name": "SK",
"icons": [
{
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CV - Stefan Kölle</title>
<title>CV - Stefan Koelle</title>
</head>
<body>
<div id="container">
@@ -14,7 +14,7 @@
Senior Software Developer & Software&nbsp;Architect
</div>
<div id="headline-photo">
<img src="../assets/stefan-koelle-foto.jpg" alt="Stefan Kölle">
<img src="../assets/stefan-koelle-foto.jpg" alt="Stefan Koelle">
</div>
</div>
{% include "cv-content.njk" %}
+1
View File
@@ -7,6 +7,7 @@ module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy({ "stefankoelle/.htaccess": ".htaccess" });
eleventyConfig.addPassthroughCopy({ "stefankoelle/pdf/cv.pdf": "pdf/cv.pdf" });
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/fonts/lobster-latin.woff2": "fonts/lobster-latin.woff2" });
eleventyConfig.addPassthroughCopy({ "shared/theme-hub.css": "theme.css" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/hub.svg": "favicon.svg" });
+1 -1
View File
@@ -1,5 +1,5 @@
---
title: "Stefan Kölle"
title: "Stefan Koelle"
description: "Experienced Senior Software Developer and Software Architect with over 20 years of experience. Specialized in .NET and Kubernetes, with hands-on experience in AI-assisted development using Claude Code."
layout: stefankoelle.njk
---
+2 -2
View File
@@ -8,7 +8,7 @@ description: "WiFi-controlled LED display solution with REST API, DHT22 sensor,
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{{ description }}">
<meta name="author" content="Stefan Kölle">
<meta name="author" content="Stefan Koelle">
<link rel="canonical" href="{{ site.url }}{{ page.url }}">
<meta property="og:title" content="{{ title }}">
<meta property="og:description" content="{{ description }}">
@@ -25,7 +25,7 @@ description: "WiFi-controlled LED display solution with REST API, DHT22 sensor,
"@type": "Article",
"headline": "{{ title }}",
"description": "{{ description }}",
"author": { "@type": "Person", "name": "Stefan Kölle" },
"author": { "@type": "Person", "name": "Stefan Koelle" },
"url": "{{ site.url }}{{ page.url }}"
}
</script>
Binary file not shown.
Binary file not shown.
Binary file not shown.
+105 -45
View File
@@ -7,6 +7,20 @@
<meta name="description" content="Explore three Pascal demo programs showcasing graphics, animation, and sound capabilities using Turbo Pascal and assembler. Learn about text and graphics animation, sound integration, and user interaction in Pascal programming.">
<link rel="canonical" href="https://www.moonweb.org/tcm/" />
<style>
@font-face {
font-family: 'VT323';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('fonts/vt323-latin.woff2') format('woff2');
}
@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('fonts/pressstart2p-latin.woff2') format('woff2');
}
body {
background-color: black;
color: #FFF741;
@@ -14,10 +28,15 @@
justify-content: center;
align-items: center;
flex-direction: column;
font-family: Arial, sans-serif;
font-family: 'VT323', monospace;
margin: 0;
padding: 0;
font-size: 18px; /* Schriftgröße des Textes */
font-size: 20px;
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}
h1, h2 {
font-family: 'Press Start 2P', cursive;
font-size: 1em;
}
.container {
text-align: center;
@@ -100,20 +119,27 @@
text-decoration: none;
cursor: pointer;
}
table {
border:0px;
border-color:#000000;
}
th {
text-align:left;
}
td {
th, td {
text-align:left;
vertical-align: top;
padding-right: 20px;
overflow-wrap: break-word;
}
@media (max-width: 768px) {
#logo {
width:100%
}
table tr {
display: flex;
flex-direction: column;
margin-bottom: 0.8em;
}
table td {
padding-right: 0;
}
table td:first-child {
margin-bottom: 0.2em;
}
}
.footer {
font-size: 0.9em;
@@ -130,8 +156,9 @@
<div class="logo">
<img id="logo" src="logo.png" alt="TranceMission Logo">
</div>
<h1>TranceMission Archive</h1>
<div class="intro">
<p>TranceMission is a group known for creating demos and intros, primarily in the early 1990s. These demos often showcased advanced graphics, sound, and programming techniques using Turbo Pascal and assembler. The group was led by Stefan Koelle, who was responsible for coding, graphics, and music. TranceMission's work was characterized by creative animations, sound effects, and interactive elements, making them a notable name in the demo scene of that era. Their productions were designed to run on DOS-based PCs, often pushing the hardware capabilities of the time.</p>
<p>TranceMission is a group known for creating demos and intros, primarily in the early 1990s. These demos often showcased advanced graphics, sound, and programming techniques using Turbo Pascal and assembler. The group was led by Stefan Koelle, who was responsible for coding, graphics, and music. TranceMission's work was characterized by creative animations, sound effects, and interactive elements, making it a notable name in the demo scene of that era. Their productions were designed to run on DOS-based PCs, often pushing the hardware capabilities of the time.</p>
<p>Source code can be found on GitHub <a href="https://github.com/skoelle/dos-tcm-intros" target="_blank">https://github.com/skoelle/dos-tcm-intros</a>.</p>
</div>
<div class="program">
@@ -143,30 +170,26 @@
<img src="play.png" alt="YouTube Logo" class="youtube-logo">
</a>
</div>
<p>This Pascal program is a demo that displays animated stars and text on the screen, along with playing sound effects.</p>
<p>This Pascal program is a demo that displays animated stars and text on the screen and plays sound effects.</p>
<table>
<tr>
<th>Component</th>
<th>Description</th>
</tr>
<tr>
<td>Initialization</td>
<td><b>Initialization</b></td>
<td>Sets up graphics mode and clears the screen.</td>
</tr>
<tr>
<td>Text Display</td>
<td>Uses <code>writeline</code> procedure to display text.</td>
<td><b>Text Display</b></td>
<td>Uses the writeline procedure to display text.</td>
</tr>
<tr>
<td>Animation</td>
<td><b>Animation</b></td>
<td>Animates stars moving across the screen.</td>
</tr>
<tr>
<td>Sound</td>
<td><b>Sound</b></td>
<td>Plays sound if a SoundBlaster card is detected.</td>
</tr>
<tr>
<td>User Interaction</td>
<td><b>User Interaction</b></td>
<td>Waits for a key press to exit the demo.</td>
</tr>
</table>
@@ -184,28 +207,24 @@
<p>This Pascal program is a demo that displays animated text and graphics with sound effects.</p>
<table>
<tr>
<th>Component</th>
<th>Description</th>
</tr>
<tr>
<td>Initialization</td>
<td><b>Initialization</b></td>
<td>Sets up graphics mode and clears the screen.</td>
</tr>
<tr>
<td>Text Display</td>
<td>Uses <code>writeline</code> and <code>writeline2</code> procedures to display text.</td>
<td><b>Text Display</b></td>
<td>Uses the writeline and writeline2 procedures to display text.</td>
</tr>
<tr>
<td>Animation</td>
<td><b>Animation</b></td>
<td>Animates text using a sine wave effect. Speed is controlled by 'S' and 'A' keys.</td>
</tr>
<tr>
<td>Sound</td>
<td><b>Sound</b></td>
<td>Plays sound if a SoundBlaster card is detected. Volume is controlled by '+' and '-' keys.</td>
</tr>
<tr>
<td>User Interaction</td>
<td>Responds to key presses and exits on ESC, Enter, or Space key.</td>
<td><b>User Interaction</b></td>
<td>Exits when ESC, Enter, or Space is pressed.</td>
</tr>
</table>
</div>
@@ -221,31 +240,27 @@
</a>
</div>
<p>This Pascal program is a demo that displays a menu with animated text and graphics, along with playing sound effects.</p>
<p>This Pascal program is a demo that displays a menu with animated text and graphics and plays sound effects.</p>
<table>
<tr>
<th>Component</th>
<th>Description</th>
</tr>
<tr>
<td>Initialization</td>
<td><b>Initialization</b></td>
<td>Sets up graphics mode and clears the screen.</td>
</tr>
<tr>
<td>Text Display</td>
<td>Uses <code>writeline</code> procedure to display text.</td>
<td><b>Text Display</b></td>
<td>Uses the writeline procedure to display text.</td>
</tr>
<tr>
<td>Animation</td>
<td><b>Animation</b></td>
<td>Animates a logo and text with a wobble effect.</td>
</tr>
<tr>
<td>Sound</td>
<td><b>Sound</b></td>
<td>Plays sound if a SoundBlaster card is detected.</td>
</tr>
<tr>
<td>User Interaction</td>
<td>Allows navigation through a menu using the keyboard and exits the demo on specific key presses.</td>
<td><b>User Interaction</b></td>
<td>Allows navigation through a menu using the keyboard and exits the demo on the exit menu point.</td>
</tr>
</table>
</div>
@@ -262,6 +277,51 @@
<iframe class="lightbox-content" id="lightbox-video" width="560" height="315" src="" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
</div>
<canvas id="starfield" style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;pointer-events:none;"></canvas>
<script>
(function() {
const canvas = document.getElementById('starfield');
const ctx = canvas.getContext('2d');
let w, h, scrollY = 0;
function resize() { w = canvas.width = window.innerWidth; h = canvas.height = window.innerHeight; }
resize();
window.addEventListener('resize', resize);
window.addEventListener('scroll', function() { scrollY = window.scrollY; }, { passive: true });
const layers = [
{ count: 80, speed: 0.3, sizeMin: 0.5, sizeMax: 1.2, alpha: 0.4 },
{ count: 50, speed: 0.7, sizeMin: 1.0, sizeMax: 2.0, alpha: 0.6 },
{ count: 25, speed: 1.5, sizeMin: 1.5, sizeMax: 3.0, alpha: 0.9 },
];
const stars = [];
for (const layer of layers) {
for (let i = 0; i < layer.count; i++) {
stars.push({
x: Math.random() * w,
y: Math.random() * h,
speed: layer.speed + Math.random() * 0.3,
size: layer.sizeMin + Math.random() * (layer.sizeMax - layer.sizeMin),
alpha: layer.alpha * (0.7 + Math.random() * 0.3),
});
}
}
function draw() {
ctx.clearRect(0, 0, w, h);
for (const s of stars) {
s.x += s.speed;
if (s.x > w + 5) { s.x = -5; s.y = Math.random() * h; }
const y = ((s.y - scrollY * 0.02) % h + h) % h;
ctx.beginPath();
ctx.arc(s.x, y, s.size, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,255,255,' + s.alpha + ')';
ctx.fill();
}
requestAnimationFrame(draw);
}
draw();
})();
</script>
<script>
function openLightbox(src) {
document.getElementById('lightbox-img').src = src;