I am building a fully functional, retro-styled Operating System simulator running entirely in the browser
I am building a fully functional, retro-styled Operating System simulator running entirely in the browser
So this time I fully restructured the MS-DOS Prompt.
The old terminal implementation had slowly turned into a monolith, with the UI, I/O, and command logic all packed into a single file and handled through a massive switch statement. I refactored the entire system and moved to a Command Pattern architecture. The terminal now acts purely as a controller, while individual commands live in a separate modular registry, which makes future expansion much cleaner and easier to maintain.
During the refactor, I also fixed the flexbox layout so the input line consistently stays anchored at the bottom of the log, where it should be. In addition, I implemented a custom auto-scroll function synchronized with DOM updates, preventing visual glitches when rendering long outputs.
I added several quality-of-life improvements as well. Command history is now supported through the up and down arrow keys, and I implemented a smart Tab-completion system. When typing the first token, it searches both the command registry and the Virtual File System. When typing arguments, it restricts suggestions strictly to filesystem paths.
Log in to leave a comment
Today I spent some time fixing a few bugs and polishing a couple of things.
I fixed the context menu, which wasn’t showing the child menu when clicking New. I also corrected a bug where right-clicking multiple times would spawn several menus at once, and made sure they now close properly when clicking anywhere else.
On top of that, I fixed an issue where right-clicking an icon would select multiple items instead of just one. To round things out, I added Ctrl+Click and Shift+Click support for desktop icon selection, which makes the whole experience feel a lot more natural.
Oh and I also added group dragging for desktop icons. When multiple items are selected, dragging one icon now moves the entire group while keeping their relative positions intact.
Log in to leave a comment
Finally… after 8 and a half hours… Solitaire is done. I genuinely didn’t expect this game to take anywhere near that long. I thought it would be simple. It absolutely was not. I may or may not regret listening to Foxreef and their request to add Solitaire.
I started by building a custom drag and drop physics engine. I relied on native mouse events with my own hit-testing to detect collisions with tableau columns and foundation slots. To make everything feel smooth and correct, I added snap-back logic that animates invalid moves back to their original position, strictly following the rules defined in logic.js.
On the visual side, I implemented a sprite sheet renderer. All cards are rendered from a single cards.png asset, with the engine dynamically calculating background-position values to display the correct card. Credit for the sprite sheet goes to https://www.spriters-resource.com/pc_computer/solitaire/asset/107016/
.
The hardest problem by far was game over detection. Basic logic wasn’t enough, since players could endlessly move cards between empty columns without actually making progress. To fix this, I built a predictive simulation system. It works like a depth-limited search that simulates every legal move in memory and analyzes whether those moves reveal hidden cards or clear columns. If the deck is empty and the simulation finds no moves with strategic value, the game correctly triggers a loss state.
To wrap it all up and make the game replayable, I added a stats and high score system. The UI now includes a retro-style stats bar that tracks play time. The timer only starts on the first user interaction, so players can think through their opening move without being penalized. Best times are saved using LocalStorage, just like in the Snake game.
(The entire operating system runs smoothly. In the videos it sometimes stutters or seems slow, but it’s just OBS).
Let’s see if you can beat those 7:02 minutes.
Log in to leave a comment
According to Foxreef in a previous devlog, having Solitaire is basically mandatory before this project can be considered “finished,” so I finally started working on it.
I began by setting up the core game logic and architecture. I defined the main game state structures for the deck, waste, foundations, and tableau, which handle how cards move through the game.
For shuffling, I implemented the Fisher–Yates algorithm to make sure the deck is properly randomized and fair every time. I also coded the dealing logic so the seven columns are populated correctly, with the right mix of face-up and face-down cards.
Before touching the UI, I ran logic tests to verify the deck count and card distribution were correct, just to make sure everything was solid at the core level.
Log in to leave a comment
This update was mostly about improving window controls and making desktop interactions feel more complete.
I added full window resizing support to the window manager. Windows now have resize handles on all edges and corners, handled through invisible DOM elements that track mouse movement. The resizing logic recalculates size and position in real time, so windows can be expanded in any direction while still respecting minimum size limits.
On the desktop side, I implemented a selection box, the classic click-and-drag rubber banding tool. While dragging, a semi-transparent blue rectangle follows the cursor, and I added AABB-based collision detection so icons are selected as soon as the box intersects with them.
I also upgraded the context menu to support batch operations. The system now understands when you right-click a group of selected icons, and actions like Open or Delete are applied to the entire selection instead of just a single file.
The Image Viewer app got a small but useful upgrade too. It now preloads images to read their natural width and height, then automatically resizes and centers the window so the content fits perfectly.
Finally, I gave the Shut Down button a real purpose. It’s no longer just visual, it now acts as a hard reset for the system. Clicking it wipes the LocalStorage data and reloads the page, effectively formatting the virtual drive and giving you a clean slate instantly.
I spent this update expanding the file system and tightening up how the UI behaves.
The virtual file system now supports full CRUD operations. Files and folders can be created, read, updated, deleted, and renamed directly by the system. I added mkdir, delete, and rename logic at the kernel level so the GUI can interact with the file system properly.
I also implemented a dynamic context menu system that detects what you right-click on, whether that’s the desktop, an icon, or the taskbar. The menu renderer now supports cascading submenus, so things like New > Folder work as expected. Menus also handle screen boundaries correctly and flip direction automatically to stay on-screen.
On the desktop side, I upgraded the icon layout logic with a smarter grid system. Before placing a new file, the system checks which grid slots are already occupied and picks the first available one, preventing overlap. Icon positions are also written to VFS metadata now, so the desktop layout persists across reboots.
This time I went back to the Snake app. The old version was way too basic, so I rebuilt it from scratch as a proper game engine.
To make it feel more like a console game, I added some actual game juice. There’s a particle system now, so pixels explode when you score, and the screen shakes when you crash. I also built a custom 8-bit synthesizer using the Web Audio API, which generates retro sounds in real time, kind of like a Game Boy.
I added a level generator that changes the map every 100 points. In later levels, the game switches into a Chaos Mode, adding random obstacles while still preventing impossible or unfair spawns. On top of that, the game now connects to the virtual file system to save your high score.
For this demo, I lowered the difficulty cap from 100 to 50 points so it’s easier to show all the levels quickly. The recording runs a bit choppy, but the gameplay itself is smoother.
Log in to leave a comment
The OS has finally moved from a stateless demo into something that actually remembers itself. The old “amnesia” problem, where everything reset on reload, is gone.
I reworked the virtual file system so it now talks directly to the browser’s LocalStorage. On boot, the system checks if a valid file structure already exists. If it does, it loads the previous state. If not, it creates a fresh default image. This means files and directory changes now persist properly between sessions.
I also extended persistence to the desktop itself. Each icon now stores its own position using metadata with x and y coordinates. When you move an icon, the physics engine snaps it to the grid and immediately saves the new position to the virtual disk. Reloading the page no longer resets the layout, everything comes back exactly how you left it.
On top of that, File Explorer navigation is now fully working. Path resolution is handled correctly, so you can move into subfolders and back to the root drive without things breaking.
Log in to leave a comment
I’ve made some big progress turning the static interface into something that actually feels interactive.
The window system got a major upgrade. Windows now have fully working minimize, maximize, and close buttons. When a window is maximized, it remembers its previous size and position so it can restore correctly. Fixed-size apps like the Calculator automatically disable maximize to avoid breaking their layout.
I also added internet access by building a simple Internet Explorer–style app using an internal iframe engine. Wikipedia is set as the default page since it’s stable and avoids most cross-origin issues.
The desktop itself is now fully interactive. Icons are no longer hardcoded, they’re generated directly from the virtual file system. You can drag icons around freely using a custom physics system, and when you drop one, it snaps to the nearest grid cell to keep things aligned. Icons can’t overlap each other, and if you try to drop one on top of another, it snaps back. I also added boundary checks so icons can’t be dragged off-screen or behind the taskbar.
There were a few technical hurdles along the way. Early on, browser integration was unstable and iframe failures could force a full OS reload, which I fixed by isolating iframe error handling. I also had a grid math bug that caused icons to shift slightly when released, and a small event listener typo that made icons stick to the cursor. Fixing those issues made the whole interaction system much more stable.
Log in to leave a comment
Hello again. I haven’t done a ton since the last update, but here are a few things I worked on.
I expanded the system’s software library and fixed a pretty critical startup issue. I built a fully functional Calculator app. To get the layout right, I moved away from Flexbox and switched to CSS Grid, which fits the keypad layout perfectly. I also added a safe arithmetic engine that handles normal operations and edge cases, like dividing by zero, which now triggers a system error beep.
I also fixed a major boot bug where the OS would fail to load completely if the user didn’t interact with the page, like clicking during startup to allow the sound to play. The issue was that the boot sequence was awaiting the startup sound. Since modern browsers block audio autoplay until there’s user interaction, the await caused the entire JavaScript execution to hang indefinitely, waiting for permission that never arrived.
The fix was to refactor the audio system into a non-blocking, fire-and-forget approach. The OS now tries to play the sound but immediately continues loading the desktop, so it always boots correctly even if audio is blocked.
Now here are some pictures of the calculator…
Log in to leave a comment
I’ve “successfully” implemented the first native game: a working version of Snake. It runs on a custom game loop and handles basic collisions, although it still needs some polish to feel just right.
I also added sound effects to the game, with beeps for eating apples and for the game over state. On top of that, Snake is now available directly from the Start Menu.
Log in to leave a comment
I expanded the system to handle multimedia and spent some time polishing the core user experience.
I built a native Image Viewer app so the system can display graphics. To make that work cleanly, I added a strategy-based approach in the desktop logic that checks file extensions and launches the correct app automatically. Image files open in the Viewer, and text files open in Notepad.
I also refined the window manager and its connection to the taskbar. Taskbar buttons now respond to window focus and appear pressed when a window is active. On top of that, I optimized the drag-and-drop physics engine and fixed a potential memory leak by switching to dynamic event listeners that only exist while a window is being dragged.
Finally, I added audio. I built a custom audio subsystem using the Web Audio API, so sounds are generated in real time instead of playing external MP3 files. The system now produces a startup chord during boot and classic system beeps.
(Turn the volume up to hear the startup sound.)
Log in to leave a comment
This time I focused on tying the core apps into the file system so they actually work together instead of feeling separate.
I upgraded the virtual file system so it can create and save new files dynamically, not just read existing ones. The command prompt is now fully wired into the system as well. It supports basic commands like cd for changing directories, dir or ls for listing files, and cat for reading file contents. It keeps track of your current path and shows clear red error messages when something goes wrong.
I also finished the Notepad integration. I can now write text and save it as a real file, for example /test.txt, directly to the virtual disk. The full loop is finally there: create a file in Notepad, then find and read it using the Terminal.
Log in to leave a comment
I pushed the system a bit further by adding a working command line and cleaning up some of the visual details.
I built a functional Command Prompt that can interpret real text commands like help, date, and cls directly from the Run box, which gives the system a proper CLI feel. On the visual side, I updated the boot log to be more historically accurate by listing real 1995-era hardware, and I also added a wallpaper to finish off the desktop look.
Log in to leave a comment
The system finally has a complete navigation flow. This time I focused on window management and on building one of the most iconic features of the 90s: the Start Menu.
I upgraded the window manager to properly handle window states. Windows can now be minimized to the taskbar and restored with a click, so it’s no longer just about opening and closing things. It actually feels like real multitasking.
I also recreated the classic Start Menu layout, including the vertical gradient banner and the specific hover effects. Then I wired everything together so it all actually works. Clicking the Start button opens the menu, and the menu items launch the apps I built earlier, like Documents and Notepad. Notepad can now create new files on the fly, and there’s even a Shut Down option that reboots the session.
Log in to leave a comment
I’ve turned the system from a read-only viewer into an actual workspace that can create and modify data.
The virtual file system now supports writing, not just reading. Files can be edited and updated directly in memory. On top of that, I built the first real application for the OS: a fully functional Notepad. It can open text files, let me edit them, and save the changes back to the virtual disk.
I also hooked desktop icons into the app logic. Double-clicking a text file now automatically opens it in Notepad with its contents loaded, and clicking a folder opens a directory view recursively.
Finally, I added proper taskbar integration. The window manager now stays in sync with the taskbar, adding and removing buttons at the bottom of the screen as windows are opened and closed, so multitasking actually feels real.
Log in to leave a comment
I’ve officially named the project Web95. Along with the rebranding, I focused on giving the OS some actual memory and a working desktop interface instead of just visuals.
I built a virtual file system using a JSON-based structure that simulates a real C: drive. The system can now read directories, access files, and keep everything in memory. I then wired the desktop directly to that file system, so icons on the desktop are generated automatically based on the files that actually exist.
I also added the classic interaction behavior: clicking an icon highlights it in blue, and double-clicking hands things off to the window manager to open the file or folder.
No video this time, you’ll have to use your imagination. In the first image, the System folder is selected, and in the second one you can see two folders and a file opened.
Log in to leave a comment
I’ve updated the project from a static boot screen into a working GUI. The window rendering is now fully dynamic using the DOM, with CSS box-shadows to get that classic retro 3D look. I also added a taskbar at the bottom with a Start button and a real-time clock.
I implemented my own drag-and-drop logic to handle window movement, tracking the mouse and updating positions instantly. On top of that, there’s now a focus system, so clicking a window brings it to the front and highlights the title bar properly.
This time I did record a video showing everything in action, and I also came up with a name for the project: Web95.
One thing I wanted to mention is the time tracking. I logged 4 hours and 30 minutes of work (HackTime shows it, and I timed it myself too), but when I upload the vlog on hackclub.com, it says: “This devlog will log 2h 32m of work.” That’s almost two hours less, which feels like a pretty big difference.
Log in to leave a comment
The code currently runs a simulated BIOS boot sequence using a custom-made DOM engine. It asynchronously renders system checks text line-by-line and, once the ‘hardware’ check is complete, it triggers a state change that initializes the graphical interface. (I couldn’t record so I’m just showing the part when the check it’s completed).
For now, the system check info is hardcoded.
Log in to leave a comment