Written by GuillaumeTechProject Deep Core

Deep Core, part 1: from an HTML prototype to LÖVE

Where my idle mining game comes from: a one-page prototype, a Lua port checked number for number against it, and the first changes that went past it.

Deep Core in October 2026, before the redesign: the Mine tab with the 500-level shaft and more than twenty rigs drilling side by side, the rock to strike on the left and the rig upgrades on the right.

Deep Core is my idle mining game. You strike a rock by hand, buy rigs that dig a shaft, feed the ore to a refinery, and when the run slows down you collapse the mine to start over stronger. This series tells its story from the changelog, the commit history and screenshots of the game. This first part covers the prototype, the port to Lua and the choices made in the first days. The screenshots show the game in October 2026, before its redesign.

Table of contents

The prototype

Deep Core started as a single HTML page built with React. The whole game fit in it, in four tabs: Mine, Refinery, Field Perks and Legacy. The shaft was 50 levels deep, with seven soils from Topsoil and its coal down to the Core and its Aetherium. Six refinery buildings turned ore into goods, from the Kiln, which makes coke out of coal, to the Gem Cutter. Field Perks, bought with geodes, were a root, three branches of five perks and a capstone, 17 in all. Legacy Perks, bought with Core Shards, had 18 laid out the same way. One of them, Night Crews, kept the mine working for up to 8 hours while the page was closed.

The loop worked, but in a browser. I wanted the game on Steam and on phones. I picked LÖVE because I already use it for Skyfall, and a .love file can be packaged for Windows, macOS, Linux, Android and iOS. Deep Core needs no network and no native module, so a Steam build only has to launch the packaged game.

A port, not a rewrite

The first rule of the port was to change nothing. Every balance number from the prototype went into one Lua file, data.lua. The game logic, a single JavaScript class in the prototype, became state.lua, ported line by line: rigs, shaft, bays, buildings, perks and collapse. In an idle game, costs and income grow exponentially, so a small difference in one formula becomes a large one after a few hours of play. A port that “looks about right” can still play differently.

The Refinery tab before the redesign: six building cards in two rows, Kiln, Wire Mill, Foundry, Mint, Refinery and Gem Cutter. Each card shows what the building needs and gives, its profit per batch, and its Speed, Quality and Resource optimisation upgrades.
The refinery, still laid out like the prototype: one card per building.

Proving it

To check the port, I wrote a parity harness on the first day. tools/parity pulls the game class out of the prototype’s HTML file and runs it in Node. It runs the Lua port in a plain lua interpreter. Both go through the same scenario of 782 steps, from a fresh start down to the 50-level floor, with every perk bought and four collapses. The harness compares 5,310 values from each run: balances, inventories, prices, rig and building state, costs, perk labels and the foreman’s log. On day one, they matched bit for bit.

That harness shaped the code. state.lua and data.lua don’t touch LÖVE at all, which is what lets them run under plain Lua. When I change the balance on purpose, the change goes into a DEVIATIONS list, and the harness patches it into the prototype before comparing. Everything outside that list is still compared exactly. The harness still runs today, on 8,502 values per snapshot. The largest relative difference is 2e-16, from the logarithm in the shard formula.

One layout, every screen

The prototype was designed at 1440×960. The port draws in logical units at that size and applies one scale factor per frame, so on a desktop it is 1:1 with the prototype. Below about 980 logical units of width, the layout stacks. The sidebar with the rock and the stockpile becomes its own Camp tab, the four readouts drop into a 2×2 grid, and the tab strip wraps. The game rasterises text at the screen’s real resolution, so it stays sharp at any scale. When a phone rotates mid-run, the game picks up the new size on the next frame.

The same night, a landscape phone at 932×430 zoomed in past what fit. The fix takes height into account when picking the scale. Below 560 logical units of height, the header puts the title and tabs on one row and the readouts underneath, which cuts it from 180 to 110 units.

To catch the rest, the port got a smoke test. love . --smoke draws 93 layout permutations, then fires about 41,000 synthetic clicks over a grid on every tab and checks that all 14 UI effects actually happen: striking, selling, buying, switching tabs, collapsing and the rest. A button that draws fine but does nothing is easy to miss on a window size I never try by hand.

Time away

An idle game has to keep going while it’s closed. Every save records the wall-clock time. On load, the game replays the gap through the same simulation step the live game uses, so an hour away gives exactly what an hour of play would have given. The tick also follows the wall clock instead of frame time. A minimised window or a locked phone catches up on the next frame, capped at one hour per tick, as in the prototype.

The save itself is a Lua table, loaded in an empty sandbox. A corrupted or hand-edited file falls back to a fresh run instead of running anything.

On the afternoon of September 30, the game got its “While you were away” window. It opens on launch and lists the money and geodes the absence earned, and the extra Core Shards a collapse would now pay. Night Crews also moved to the first node of the Fortune branch, at 2 shards. It was the first entry in the DEVIATIONS list.

Past the prototype

On October 3, the port started to go further than the original.

  • Smoother numbers. The simulation ticks every 0.2 seconds, so the drill and geode bars moved in visible jumps. They now extrapolate between ticks without touching the game state. Money, stock and stats roll toward their new value, the mouse wheel eases toward its target, and the “While you were away” window counts its totals up from zero.
  • 500 levels. The prototype stops at level 50. The port adds eight deep ores past it, from Voidstone at level 51 down to Singularium at 443. Each one unlocks a deep building that has to be invented. Its ore must be reached, and the buildings making its inputs must already be built. The depth limit opens 10 levels at a time.
  • Stock kept for the buildings. Auto-sell only held back the inputs of deep buildings, so Coke sold out before the Foundry, which needs it for steel, could use it. Auto-sell and “Sell all” now keep what every running building needs.
  • More rigs. Rig bays were capped at 8. Now a breakthrough that passes the middle of a stratum without reaching new soil opens one more bay, priced at 1.5× the previous rig. The shaft view scales up to 28 rigs.
  • Night Crew and the Crews branch. Night Crew became a Field Perk, second in the Trade branch, for 5 geodes, with a 12-hour window. A fourth Legacy branch, Crews, got ten perks that each double that window. Saves that owned the old Night Crews got their 2 shards back.
  • Seed Money. The Legacy perk that sets a new run’s starting cash went from $25K to $250M.
  • Perk webs. The perk lists became webs, in the style of a passive skill tree. Field and Legacy Perks are now 84 nodes each, around a free Start node. Four arms leave Start, each with side branches, joined by bridge nodes and an outer ring with four keystones. Every perk from the prototype kept its id, cost and effect, so the harness still checks them. The new minor, notable and keystone nodes add stat bonuses. You drag the canvas to pan, zoom with the wheel or a pinch, and hover a node to see what it gives.

The Field Perks tab before the redesign: a web of nodes in green, amber, yellow and copper around Foreman's Post, with a popover on Tempered Bits that reads "Rigs drill 30% faster. Owned".
Field Perks as an 84-node web, bought with geodes and lost when the mine collapses.

The Legacy tab before the redesign: a purple "Collapse the mine" panel offering 2,878 shards, above the Legacy Perks web around The Old Shaft, with a popover on the Heart of the World keystone.
The Legacy tab: collapse for Core Shards, then spend them on perks that survive every collapse.

Then nothing for three weeks. The next commit, on October 24, replaced the whole interface.

By the numbers

  • 22 commits between September 30 and October 3.
  • About 4,000 lines of Lua at the end of September 30, and 5,100 on October 3.
  • 782 steps and 5,310 compared values in the first parity scenario.

The Deep Core story

  1. From an HTML prototype to LÖVE (this article)
  2. Pixel art, a crew and a freight yard