Remove extra tags

This commit is contained in:
Andras Schmelczer 2026-06-25 23:41:46 +02:00
parent 11796a8869
commit 82c723c192
18 changed files with 4 additions and 93 deletions

View file

@ -13,16 +13,11 @@ links:
url: https://github.com/schmelczer/backup-container/pkgs/container/backup-container url: https://github.com/schmelczer/backup-container/pkgs/container/backup-container
article: article:
tags: ['systems', 'tools'] tags: ['systems', 'tools']
role: Container and script author
stack: ['Bash', 'BorgBackup', 'BTRFS', 'Alpine', 'Docker', 'SSH', 'zstd'] stack: ['Bash', 'BorgBackup', 'BTRFS', 'Alpine', 'Docker', 'SSH', 'zstd']
scale: One container, multiple targets per host, two years of restored incidents scale: One container, multiple targets per host, two years of restored incidents
outcome: A self-hosted backup that has survived every actual incident I've thrown at it outcome: A self-hosted backup that has survived every actual incident I've thrown at it
audience: technical
project: project:
title: Backup Container title: Backup Container
description: A Bash container around BorgBackup. BTRFS snapshot for atomic consistency, numeric env vars for multi-target 3-2-1, sleep-loop instead of cron.
thumbnail:
alt: Placeholder thumbnail for the backup container project.
--- ---
Once you self-host a few services with live databases, the backup question stops being theoretical: everything on the box is mid-write at every moment of the day. This container is my answer, two years and several real restores in (including the photo library behind the [e-ink frame](/articles/frame-eink-photo-display/)). One Alpine container and four short shell scripts (the longest is 84 lines) push a BTRFS snapshot to one or more [Borg](https://borgbackup.readthedocs.io/) repositories on a fixed interval. Multi-target is numeric env vars (`BORG_REPO_0`, `BORG_REPO_1`, ...); there's no config format and no DSL, because the env file is the configuration. The design has exactly one clever moment, the snapshot, and I've worked to keep everything else too simple to break. Once you self-host a few services with live databases, the backup question stops being theoretical: everything on the box is mid-write at every moment of the day. This container is my answer, two years and several real restores in (including the photo library behind the [e-ink frame](/articles/frame-eink-photo-display/)). One Alpine container and four short shell scripts (the longest is 84 lines) push a BTRFS snapshot to one or more [Borg](https://borgbackup.readthedocs.io/) repositories on a fixed interval. Multi-target is numeric env vars (`BORG_REPO_0`, `BORG_REPO_1`, ...); there's no config format and no DSL, because the env file is the configuration. The design has exactly one clever moment, the snapshot, and I've worked to keep everything else too simple to break.

View file

@ -8,15 +8,10 @@ thumbnail:
alt: Screenshot of a Unity traffic simulation. alt: Screenshot of a Unity traffic simulation.
article: article:
tags: ['simulation', 'systems'] tags: ['simulation', 'systems']
role: Simulation author
stack: ['Unity', 'C#', 'REST API', 'Blender'] stack: ['Unity', 'C#', 'REST API', 'Blender']
outcome: Visible consequences for an otherwise abstract PLC challenge outcome: Visible consequences for an otherwise abstract PLC challenge
audience: technical
project: project:
title: City Simulation title: City Simulation
description: A Unity city where REST-controlled traffic lights made bad PLC code visible as car crashes.
thumbnail:
alt: Screenshot of a Unity city traffic simulation.
--- ---
Most security challenges punish a wrong answer with a red "incorrect". This one punished it with car wrecks. For a PLC cybersecurity event in the summer of 2018, I built a small Unity city where the traffic lights were driven over a REST API by whatever control logic the contestants wrote. Good logic produced boring, flowing traffic. Bad logic produced intersections that people gathered around to watch. Most security challenges punish a wrong answer with a red "incorrect". This one punished it with car wrecks. For a PLC cybersecurity event in the summer of 2018, I built a small Unity city where the traffic lights were driven over a REST API by whatever control logic the contestants wrote. Good logic produced boring, flowing traffic. Bad logic produced intersections that people gathered around to watch.

View file

@ -14,11 +14,9 @@ links:
download: true download: true
article: article:
tags: ['games', 'web', 'systems'] tags: ['games', 'web', 'systems']
role: Game and backend systems author
stack: ['TypeScript', 'Node.js', 'WebSockets', 'WebGL', 'SDF-2D'] stack: ['TypeScript', 'Node.js', 'WebSockets', 'WebGL', 'SDF-2D']
scale: Game servers ticking at 200 Hz, each serving 1632 clients at 25 updates a second, browser and mobile scale: Game servers ticking at 200 Hz, each serving 1632 clients at 25 updates a second, browser and mobile
outcome: A multiplayer browser game that proved SDF-2D survived a real game loop outcome: A multiplayer browser game that proved SDF-2D survived a real game loop
audience: technical
media: media:
- type: image - type: image
src: ./_assets/decla-red.jpg src: ./_assets/decla-red.jpg
@ -26,7 +24,6 @@ article:
caption: Two teams, small planets, real gravity. The renderer underneath is the SDF-2D library from my thesis. caption: Two teams, small planets, real gravity. The renderer underneath is the SDF-2D library from my thesis.
project: project:
title: decla.red title: decla.red
description: A multiplayer space shooter where client and server import the same rules package, the server ticks at 200 Hz, and the wire format is class names.
selected: true selected: true
technologies: ['TypeScript', 'Node.js', 'WebSockets', 'WebGL'] technologies: ['TypeScript', 'Node.js', 'WebSockets', 'WebGL']
thumbnail: thumbnail:

View file

@ -13,16 +13,11 @@ links:
url: https://home.schmelczer.dev/git/andras/fizika url: https://home.schmelczer.dev/git/andras/fizika
article: article:
tags: ['web', 'tools'] tags: ['web', 'tools']
role: Question database, frontend, backend
stack: ['jQuery', 'vanilla HTML/CSS', 'Node/Express', 'JSON', 'localStorage'] stack: ['jQuery', 'vanilla HTML/CSS', 'Node/Express', 'JSON', 'localStorage']
outcome: A free practice app real students still find when they search for past érettségi physics papers outcome: A free practice app real students still find when they search for past érettségi physics papers
audience: general
project: project:
title: Fizika title: Fizika
description: 'I needed it for my own physics érettségi: 682 past-paper questions, jQuery, localStorage, no accounts. Eight years on, students still find it.'
technologies: ['jQuery', 'HTML/CSS', 'Node/Express', 'JSON', 'localStorage'] technologies: ['jQuery', 'HTML/CSS', 'Node/Express', 'JSON', 'localStorage']
thumbnail:
alt: Screenshot of the Fizika practice app showing topic-selection buttons.
--- ---
I needed it. In my last year of high school I was about to sit the advanced-level (_emelt szintű_) physics érettségi, and the practice material I could find online was either paywalled or scattered across PDFs that wouldn't tell you whether your answer was right. So one evening in 2017 I started typing past exam questions into a JSON file. I needed it. In my last year of high school I was about to sit the advanced-level (_emelt szintű_) physics érettségi, and the practice material I could find online was either paywalled or scattered across PDFs that wouldn't tell you whether your answer was right. So one evening in 2017 I started typing past exam questions into a JSON file.

View file

@ -14,11 +14,9 @@ links:
article: article:
iframeThumbnail: true iframeThumbnail: true
tags: ['graphics', 'simulation', 'web'] tags: ['graphics', 'simulation', 'web']
role: Graphics and shader author
stack: ['TypeScript', 'WebGPU', 'WGSL', 'Compute shaders', 'Vite', 'Tweakpane'] stack: ['TypeScript', 'WebGPU', 'WGSL', 'Compute shaders', 'Vite', 'Tweakpane']
scale: One HTML file, 10 WGSL shaders, 6 vibe presets, up to 1.5M agents, 60 FPS target on consumer hardware scale: One HTML file, 10 WGSL shaders, 6 vibe presets, up to 1.5M agents, 60 FPS target on consumer hardware
outcome: A browser drawing toy where user strokes seed an agent simulation that overwrites them outcome: A browser drawing toy where user strokes seed an agent simulation that overwrites them
audience: technical
media: media:
- type: image - type: image
src: ./_assets/fleeting-garden.jpg src: ./_assets/fleeting-garden.jpg
@ -26,7 +24,6 @@ article:
caption: A snapshot from one session. What you see is the trail texture; the agents that drew it are already gone. caption: A snapshot from one session. What you see is the trail texture; the agents that drew it are already gone.
project: project:
title: Fleeting Garden title: Fleeting Garden
description: A single-file WebGPU drawing toy. Your strokes seed a swarm of up to a million agents; nine numbers per vibe give each preset its personality.
selected: true selected: true
--- ---

View file

@ -8,15 +8,10 @@ thumbnail:
alt: Chart comparing predicted and actual EUR/USD exchange rates. alt: Chart comparing predicted and actual EUR/USD exchange rates.
article: article:
tags: ['systems', 'tools'] tags: ['systems', 'tools']
role: Experiment author
stack: ['Python', 'NumPy', 'SciPy', 'Flask', 'MQL4'] stack: ['Python', 'NumPy', 'SciPy', 'Flask', 'MQL4']
outcome: A prediction server, an MQL4 trading client, and a clearer view of how far my edge wasn't outcome: A prediction server, an MQL4 trading client, and a clearer view of how far my edge wasn't
audience: technical
project: project:
title: Foreign Exchange Prediction Experiment title: Foreign Exchange Prediction Experiment
description: A Hanning-windowed STFT experiment on EUR/USD. Passable backtest, sober conclusions, no real money risked.
thumbnail:
alt: Chart from a foreign exchange prediction experiment.
--- ---
In the autumn of 2019 I was an undergrad with a few free weekends and the quiet conviction that I could find a small edge on EUR/USD. The screenshots that survive are flattering: the predicted rate in blue hugging the actual rate in green closely enough to look like skill. It was a linear extrapolation in the frequency domain wearing a nice coat. In the autumn of 2019 I was an undergrad with a few free weekends and the quiet conviction that I could find a small edge on EUR/USD. The screenshots that survive are flattering: the predicted rate in blue hugging the actual rate in green closely enough to look like skill. It was a linear extrapolation in the frequency domain wearing a nice coat.

View file

@ -11,7 +11,6 @@ links:
url: https://home.schmelczer.dev/git/andras/frame url: https://home.schmelczer.dev/git/andras/frame
article: article:
tags: ['embedded', 'systems', 'tools'] tags: ['embedded', 'systems', 'tools']
role: Frame builder and pipeline author
stack: stack:
[ [
'Python', 'Python',
@ -24,7 +23,6 @@ article:
] ]
scale: One panel, one household, ~64 refreshes a day at peak scale: One panel, one household, ~64 refreshes a day at peak
outcome: A wall-mounted photo frame that pulls from self-hosted Immich, gated on home presence, with no cloud dependencies outcome: A wall-mounted photo frame that pulls from self-hosted Immich, gated on home presence, with no cloud dependencies
audience: general
media: media:
- type: image - type: image
src: ./_assets/frame.jpg src: ./_assets/frame.jpg
@ -32,7 +30,6 @@ article:
caption: The bottom corners carry the photo's age and EXIF location. Painted as text on top, so the dither can't smear them. caption: The bottom corners carry the photo's age and EXIF location. Painted as text on top, so the dither can't smear them.
project: project:
title: Frame title: Frame
description: A LAN-only e-ink photo frame. Pulls from self-hosted Immich, gated on Home Assistant presence, Atkinson-dithered to 6 colours, no cloud.
selected: true selected: true
technologies: technologies:
[ [

View file

@ -1,26 +0,0 @@
---
title: A JavaFX Editor for the Cooling Simulator
description: Companion editor for the cooling-system sim. Drag-and-drop plant layout, JSON export, upload-to-backend. The smallest tool, and the one the event leaned on.
date: 2026-04-25
period: 'October-November 2018'
thumbnail:
src: ./_assets/process-simulator-input.jpg
alt: JavaFX graph editor for the cooling system simulator.
article:
tags: ['simulation', 'tools']
role: Editor author
stack: ['JavaFX', 'JSON', 'REST API']
outcome: A drag-and-drop graph editor that let non-developers feed the simulator
audience: technical
project:
title: Graph Editor
description: A drag-and-drop JavaFX editor that let event organisers reconfigure the cooling sim without me sitting next to them.
thumbnail:
alt: JavaFX editor interface for the cooling system simulator input graph.
---
The brief was one sentence: non-technical event organisers needed to rewire a simulated cooling plant, live, without me hovering behind them. That sentence quietly ruled out every interface I would have enjoyed building: config files, a DSL, anything with a curly brace in it. The [cooling-system sim](/articles/nuclear-cooling-simulation/) was only going to be as useful as the tool that fed it, so in late 2018 I built a JavaFX desktop editor: lay the plant out as a graph, drag pipes between pumps and coolers, tune each element's parameters in a side panel, then export JSON or upload it straight to the backend.
It was the smallest piece of the whole project and the piece the event quietly depended on. A simulator only its author can reconfigure is a demo; the editor is what made it a tool other people could own for a weekend.
If I built it again I'd skip JavaFX and put the editor in the browser, next to the monitoring clients. Cross-platform desktop packaging was a fight, and every install I asked of the organisers was one more reason for them to call me over, which was the exact thing the brief said to prevent.

View file

@ -17,11 +17,9 @@ links:
article: article:
featuredOrder: 1 featuredOrder: 1
tags: ['ai', 'systems', 'tools'] tags: ['ai', 'systems', 'tools']
role: Researcher and framework author
stack: ['Python', 'decorators', 'FastAPI', 'survey design'] stack: ['Python', 'decorators', 'FastAPI', 'survey design']
scale: 33 deployment habits surveyed, 6 proposed additions, framework evaluated by working data scientists and engineers scale: 33 deployment habits surveyed, 6 proposed additions, framework evaluated by working data scientists and engineers
outcome: A pip-installable framework, an MSc thesis, and one strong opinion about API surface area outcome: A pip-installable framework, an MSc thesis, and one strong opinion about API surface area
audience: recruiter-relevant
media: media:
- type: image - type: image
src: ./_assets/great-ai.png src: ./_assets/great-ai.png
@ -29,7 +27,6 @@ article:
caption: A working GreatAI service is about ten lines on top of a plain prediction function. caption: A working GreatAI service is about ten lines on top of a plain prediction function.
project: project:
title: GreatAI title: GreatAI
description: One decorator on a Python function turned it into a deployed ML service. MSc thesis with a survey to back the API choices.
selected: true selected: true
technologies: ['Python', 'ML deployment', 'API design'] technologies: ['Python', 'ML deployment', 'API design']
--- ---

View file

@ -12,11 +12,9 @@ links:
article: article:
featuredOrder: 4 featuredOrder: 4
tags: ['systems', 'web', 'tools'] tags: ['systems', 'web', 'tools']
role: Full-stack author
stack: ['Python', 'Angular', 'TypeScript', 'FastAPI', 'SQLite'] stack: ['Python', 'Angular', 'TypeScript', 'FastAPI', 'SQLite']
scale: Multi-device goal and task state shared between clients and a server scale: Multi-device goal and task state shared between clients and a server
outcome: A goal tracker still in use, and a lesson about when clever sync isn't worth it outcome: A goal tracker still in use, and a lesson about when clever sync isn't worth it
audience: recruiter-relevant
media: media:
- type: image - type: image
src: ./_assets/towers.jpg src: ./_assets/towers.jpg
@ -24,7 +22,6 @@ article:
caption: Towers of finished tasks, one column per goal. Done blocks fall into place with a small gravity animation. caption: Towers of finished tasks, one column per goal. Done blocks fall into place with a small gravity animation.
project: project:
title: Life Towers title: Life Towers
description: A multi-device goal tracker where the clever trie lost to send-the-whole-tree with a version counter. The towers were the UI; the protocol was the lesson.
selected: true selected: true
technologies: ['Python', 'Angular', 'TypeScript', 'Immutable trees'] technologies: ['Python', 'Angular', 'TypeScript', 'Immutable trees']
--- ---

View file

@ -8,15 +8,10 @@ thumbnail:
alt: RGB LED strips lit by a music synchronisation project. alt: RGB LED strips lit by a music synchronisation project.
article: article:
tags: ['systems', 'tools'] tags: ['systems', 'tools']
role: Hardware and software author
stack: ['Python', 'NumPy', 'FFT', 'Raspberry Pi', 'MOSFETs', 'vanilla web'] stack: ['Python', 'NumPy', 'FFT', 'Raspberry Pi', 'MOSFETs', 'vanilla web']
outcome: The first non-trivial project I started and finished outcome: The first non-trivial project I started and finished
audience: technical
project: project:
title: Lights Synchronized to Music title: Lights Synchronized to Music
description: Raspberry Pi music player, NumPy FFT, MOSFETs, RGB strips. The first thing I built that I actually finished.
thumbnail:
alt: RGB LED strips glowing from a music synchronization project.
--- ---
Spring 2016. I had a Raspberry Pi, a couple of 12V RGB LED strips someone had given me, a handful of MOSFETs from an electronics kit, and no idea what I was doing. I wired one of the MOSFETs backwards and it got hot enough to leave a small mark on the breadboard, which is how I learned, slowly and because I had to, to read a datasheet. This was the first thing I ever started and actually finished. Spring 2016. I had a Raspberry Pi, a couple of 12V RGB LED strips someone had given me, a handful of MOSFETs from an electronics kit, and no idea what I was doing. I wired one of the MOSFETs backwards and it got hot enough to leave a small mark on the breadboard, which is how I learned, slowly and because I had to, to read a datasheet. This was the first thing I ever started and actually finished.

View file

@ -9,11 +9,9 @@ thumbnail:
article: article:
featuredOrder: 5 featuredOrder: 5
tags: ['simulation', 'systems', 'tools'] tags: ['simulation', 'systems', 'tools']
role: Simulation and UI author
stack: ['Python', 'Flask', 'NumPy', 'HTML canvas', 'JavaFX'] stack: ['Python', 'Flask', 'NumPy', 'HTML canvas', 'JavaFX']
scale: One remote sim server, many monitoring clients, separate JavaFX graph editor scale: One remote sim server, many monitoring clients, separate JavaFX graph editor
outcome: A believable PLC simulation usable by non-specialists during a live cybersecurity challenge outcome: A believable PLC simulation usable by non-specialists during a live cybersecurity challenge
audience: recruiter-relevant
media: media:
- type: image - type: image
src: ./_assets/process-simulator.jpg src: ./_assets/process-simulator.jpg
@ -25,7 +23,6 @@ article:
caption: The JavaFX editor produced JSON that the simulator ate as input. caption: The JavaFX editor produced JSON that the simulator ate as input.
project: project:
title: Cooling System Simulation title: Cooling System Simulation
description: 'A live cooling-plant simulator for a PLC cybersecurity event. Flow as graph traversal and heat as a matrix solve: two passes instead of one PDE.'
selected: true selected: true
thumbnail: thumbnail:
src: ./_assets/nuclear-simulation.jpg src: ./_assets/nuclear-simulation.jpg
@ -48,10 +45,12 @@ As physics, this is wrong; flow and heat are coupled, and a real plant knows it.
## The editor mattered more than the solver ## The editor mattered more than the solver
The most-used interface in the whole system was the input editor, a separate JavaFX tool where organisers laid out the plant, parameterised each element, and exported the JSON the sim consumed. It earned [its own write-up](/articles/graph-editor-javafx-simulation-input/), because in hindsight it was its own project: the simulator decided what was possible, but the editor decided who could use it. The brief for the editor was one sentence: non-technical organisers needed to rewire the plant, live, without me hovering behind them. That quietly ruled out every interface I'd have enjoyed building, namely config files, a DSL, anything with a curly brace in it. So the most-used piece of the whole system ended up being a separate JavaFX desktop tool where organisers laid the plant out as a graph, dragged pipes between pumps and coolers, tuned each element's parameters in a side panel, and exported the JSON the sim consumed, or uploaded it straight to the backend.
It was the smallest piece of the project and the one the event quietly depended on. A simulator only its author can reconfigure is a demo; the editor is what made it a tool other people could own for a weekend. The simulator decided what was possible, but the editor decided who could use it.
## What I'd change ## What I'd change
- **State what the model claims.** A convincing simulator owes its audience an honest README about what it does and doesn't capture. Mine didn't have one, and anyone who took the numbers seriously could have walked away believing more than the model deserved. - **State what the model claims.** A convincing simulator owes its audience an honest README about what it does and doesn't capture. Mine didn't have one, and anyone who took the numbers seriously could have walked away believing more than the model deserved.
- **Recorded scenarios as regression tests.** Simulations drift in ways that still look plausible on screen. A few stored "this input over 60 seconds produces these outputs" runs would have caught me when I broke the temperature solver, which I did on the Saturday morning of the event, naturally. - **Recorded scenarios as regression tests.** Simulations drift in ways that still look plausible on screen. A few stored "this input over 60 seconds produces these outputs" runs would have caught me when I broke the temperature solver, which I did on the Saturday morning of the event, naturally.
- **Skip JavaFX.** Cross-platform packaging was a fight, and a web editor living next to the monitoring clients would have meant one fewer install for everyone. - **Skip JavaFX.** Cross-platform desktop packaging was a fight, and every install I asked of the organisers was one more reason to call me over, which is the exact thing the brief said to prevent. A web editor living next to the monitoring clients would have meant one fewer install for everyone.

View file

@ -11,7 +11,6 @@ links:
url: https://perfect-postcode.co.uk url: https://perfect-postcode.co.uk
article: article:
tags: ['systems', 'web', 'tools'] tags: ['systems', 'web', 'tools']
role: Server architect and operator
stack: stack:
[ [
'Rust', 'Rust',
@ -28,7 +27,6 @@ article:
] ]
scale: ~25M historical properties, ~2.5M postcodes, ~150 numeric features per row, all in RAM on a single VM scale: ~25M historical properties, ~2.5M postcodes, ~150 numeric features per row, all in RAM on a single VM
outcome: A single-binary UK property-intelligence service with sub-100ms hexagon aggregations under filter outcome: A single-binary UK property-intelligence service with sub-100ms hexagon aggregations under filter
audience: technical
media: media:
- type: image - type: image
src: ./_assets/perfect-postcode.jpg src: ./_assets/perfect-postcode.jpg
@ -36,10 +34,7 @@ article:
caption: A normal user pan triggers a hexagon aggregation under filter. The hot path holds itself to three integer compares per row. caption: A normal user pan triggers a hexagon aggregation under filter. The hot path holds itself to three integer compares per row.
project: project:
title: Perfect Postcode title: Perfect Postcode
description: A UK property-intelligence map. ~25M historical transactions, ~150 features per row, all u16-quantised in RAM, served from a single Rust binary.
selected: true selected: true
thumbnail:
alt: The Perfect Postcode dashboard with active filters on property type, price, transit time, and crime, showing a Manchester map with matching properties as a heatmap.
--- ---
A user told me the map felt sluggish when they dragged it across Manchester with four filters on. They were right, and it stung, because the previous version round-tripped to a database, decoded floats, and had spent its entire latency budget before it finished evaluating the first filter. The rewrite is one Rust binary that holds the entire UK property history in RAM and treats every filter as three integer compares. Everything else in this post follows from refusing to let that sluggishness come back. A user told me the map felt sluggish when they dragged it across Manchester with four filters on. They were right, and it stung, because the previous version round-tripped to a database, decoded floats, and had spent its entire latency budget before it finished evaluating the first filter. The rewrite is one Rust binary that holds the entire UK property history in RAM and treats every filter as three integer compares. Everything else in this post follows from refusing to let that sluggishness come back.

View file

@ -8,13 +8,10 @@ thumbnail:
alt: Screenshot of a generated photography site. alt: Screenshot of a generated photography site.
article: article:
tags: ['web', 'tools'] tags: ['web', 'tools']
role: Site generator author
stack: ['Webpack', 'Vite', 'TypeScript', 'Image processing'] stack: ['Webpack', 'Vite', 'TypeScript', 'Image processing']
outcome: A photography site that updates itself when I drop new images into a folder outcome: A photography site that updates itself when I drop new images into a folder
audience: general
project: project:
title: Photo Site Generator title: Photo Site Generator
description: Point a build script at a folder of photos, get a static site with responsive image variants. An excuse to walk with a camera.
--- ---
I take walks with a camera. Most of what I shoot isn't good, but walking slowly with a frame to think about is the most reliable way I know to come back with an idea for whatever I'm actually working on. In the summer of 2016 I wanted somewhere to put the few frames that survived, and I wasn't going to run a CMS for it. So: a Webpack script. Point it at a directory of full-size photos, get a static site with responsive variants of each. Drop a photo in, build, deploy. I take walks with a camera. Most of what I shoot isn't good, but walking slowly with a frame to think about is the most reliable way I know to come back with an idea for whatever I'm actually working on. In the summer of 2016 I wanted somewhere to put the few frames that survived, and I wasn't going to run a CMS for it. So: a Webpack script. Point it at a directory of full-size photos, get a static site with responsive variants of each. Drop a photo in, build, deploy.

View file

@ -8,15 +8,10 @@ thumbnail:
alt: Screenshot from a 3D platform game written in C. alt: Screenshot from a 3D platform game written in C.
article: article:
tags: ['games', 'systems'] tags: ['games', 'systems']
role: Game author
stack: ['C', 'SDL 1.2', 'Voxel terrain'] stack: ['C', 'SDL 1.2', 'Voxel terrain']
outcome: A playable course project, and the moment programming clicked outcome: A playable course project, and the moment programming clicked
audience: technical
project: project:
title: Platform Game title: Platform Game
description: My Basics of Programming project. 3D voxel game in C and SDL 1.2. Pointers, learned painfully.
thumbnail:
alt: Screenshot from an early 3D platform game.
--- ---
Autumn 2017, first semester, a course called Basics of Programming, and a free-choice project I aimed far too high on: a 3D voxel platformer in pure C with SDL 1.2. No engine, no scripting layer, no one to tell me that "3D in software, in C, in your first semester" was an unusual reading of the assignment. Autumn 2017, first semester, a course called Basics of Programming, and a free-choice project I aimed far too high on: a 3D voxel platformer in pure C with SDL 1.2. No engine, no scripting layer, no one to tell me that "3D in software, in C, in your first semester" was an unusual reading of the assignment.

View file

@ -20,11 +20,9 @@ links:
article: article:
featuredOrder: 2 featuredOrder: 2
tags: ['systems', 'tools', 'web'] tags: ['systems', 'tools', 'web']
role: Library author
stack: ['Rust', 'WebAssembly', 'Python', 'pyo3', 'wasm-bindgen'] stack: ['Rust', 'WebAssembly', 'Python', 'pyo3', 'wasm-bindgen']
scale: One Rust core, three published packages (crates.io, npm, PyPI), driving an Obsidian sync plugin scale: One Rust core, three published packages (crates.io, npm, PyPI), driving an Obsidian sync plugin
outcome: A small Rust library that auto-resolves prose conflicts, with WASM and Python bindings outcome: A small Rust library that auto-resolves prose conflicts, with WASM and Python bindings
audience: recruiter-relevant
media: media:
- type: image - type: image
src: ./_assets/reconcile.png src: ./_assets/reconcile.png
@ -32,7 +30,6 @@ article:
caption: reconcile-text weaves conflicting edits together instead of asking a human to choose. caption: reconcile-text weaves conflicting edits together instead of asking a human to choose.
project: project:
title: reconcile-text title: reconcile-text
description: One Rust core, three packages. Merges Markdown notes from three editors I don't control, with no operation history. Never emits markers.
selected: true selected: true
technologies: ['Rust', 'WebAssembly', 'Python', 'pyo3', 'wasm-bindgen', 'Myers diff'] technologies: ['Rust', 'WebAssembly', 'Python', 'pyo3', 'wasm-bindgen', 'Myers diff']
--- ---

View file

@ -17,7 +17,6 @@ links:
article: article:
featuredOrder: 3 featuredOrder: 3
tags: ['graphics', 'web', 'systems'] tags: ['graphics', 'web', 'systems']
role: Library author
stack: stack:
[ [
'TypeScript', 'TypeScript',
@ -28,7 +27,6 @@ article:
] ]
scale: Browser library, mobile-targeted, real-time on consumer GPUs, both WebGL1 and WebGL2 paths scale: Browser library, mobile-targeted, real-time on consumer GPUs, both WebGL1 and WebGL2 paths
outcome: An NPM package and BSc thesis; the renderer behind the decla.red multiplayer game outcome: An NPM package and BSc thesis; the renderer behind the decla.red multiplayer game
audience: recruiter-relevant
media: media:
- type: image - type: image
src: ./_assets/sdf2d.jpg src: ./_assets/sdf2d.jpg
@ -36,7 +34,6 @@ article:
caption: SDF-2D shipped as a TypeScript library, not a one-shot demo. That distinction shaped most of the design. caption: SDF-2D shipped as a TypeScript library, not a one-shot demo. That distinction shaped most of the design.
project: project:
title: SDF-2D title: SDF-2D
description: A browser 2D ray-tracer tuned for the phone in your pocket. Tile-based passes, deferred shading, shaders generated per scene and device.
selected: true selected: true
technologies: ['TypeScript', 'WebGL', 'WebGL2', 'Signed distance fields'] technologies: ['TypeScript', 'WebGL', 'WebGL2', 'Signed distance fields']
--- ---

View file

@ -13,7 +13,6 @@ links:
url: https://vault-link.schmelczer.dev url: https://vault-link.schmelczer.dev
article: article:
tags: ['systems', 'web', 'tools'] tags: ['systems', 'web', 'tools']
role: Sync engine and server author
stack: stack:
[ [
'Rust', 'Rust',
@ -29,10 +28,8 @@ article:
] ]
scale: One Rust server, one TypeScript sync engine, three published consumers (Obsidian plugin, CLI, fuzz/deterministic test harnesses) scale: One Rust server, one TypeScript sync engine, three published consumers (Obsidian plugin, CLI, fuzz/deterministic test harnesses)
outcome: A self-hosted Obsidian sync I trust enough to use as my primary vault transport outcome: A self-hosted Obsidian sync I trust enough to use as my primary vault transport
audience: technical
project: project:
title: VaultLink title: VaultLink
description: 'I refuse to give up the editor: Obsidian, Vim, VS Code, sed. Self-hosted sync that survives all four, built on reconcile-text underneath.'
selected: true selected: true
--- ---