Home js13k 2025 Post Mortem - html/css/js Game Jam
Post
Cancel

js13k 2025 Post Mortem - html/css/js Game Jam

This write-up is a bit dense, and I owe an apology in advance for the length. It is a deep dive written over many revisions after much reflection. My entry raised many questions, and I wanted to give those questions the space they deserve.

JS13k 2025 Post-Mortem: Flykt

This year I entered the JS13kGames 2025 competition with Flykt for the Black Cat theme. My game is a black-cat themed puzzle platformer. In 13 kB I packed a custom TypeScript game engine, physics, portal raycasting, a state-based animation system, procedural audio, text and graphics, and a compressed map system. On paper the technical goals were ambitious, and I met most of them. In practice, what mattered most was not the bytes I saved or the subsystems I built, but what happened once players actually played the game.

A Contrast with Last Year

The contrast is sharper than I expected when set against my first entry, Z0MB13, submitted last year (2024). Built upon the LittleJS game engine, it was technically modest to the point of austerity: a looping map, a melee stick that upgraded into firearms, some basic Perlin noise, a particle system, and a shop that sold marginally better weapons. No custom engine, no procedural pipelines, no compression gymnastics. It was my first finished game, and its primary accomplishment was simply being complete.

And yet Z0MB13 worked. Players grasped its premise in seconds, survived long enough to taste upgrades, and stayed for “one more run.” Its simplicity is what made it legible, and its upgrade loop gave players a reason to continue. Z0MB13 achieved almost nothing technically, but it held attention for the most part. This year my entry Flykt achieved far more technically, but often lost its players within the first few minutes. The divide between technical ambition and player engagement became the lens through which I came to see every mishandled decision.

Concept and Theme

For Flykt I set out to fuse two motifs: the jam’s theme, “black cat,” and portals as the foundation of a skill-based movement system. The cat would not walk in the ordinary sense. Every jump was aimed and released like a slingshot, chaining momentum through portals that clung only to black tiles. The mechanic was precise and the theme was grounded. Even the title, Flykt (Swedish for “Escape,” but also resonant with “flicked”) carried layered meaning across languages. From that seed, the game grew outward: the engine built to support expressive momentum, the art style pared down to highlighted silhouettes, the music and sound effects tuned for outward thinking. The level design became the crucible where all of this converged. Each 2D stage needed to demonstrate puzzle, rhythm and intent. In this way the game’s structure mirrored its theme: a search for escape through constraint. Early on the vision was clear and strong so the game quickly fell into place. I started playtesting levels early, setting the movement constants in stone.

Early stage interpolated rendering before/after

Early stage interpolated rendering before/after

Building the Engine

I resisted heavy frameworks and wrote everything in raw TypeScript for quick prototyping, targeting Canvas 2D. The goal was not to recreate or clone any one thing, but rather to shape a toolset that would survive both inside and outside of the 13 kB coffin of the jam. Every line had to justify itself. Assets were collapsed into a single atlas, fonts distilled into base-36 strings, and all audio generated procedurally. Even the levels leaned on Unicode glyphs as cheap graphical textures for walls and floors. The pipeline was then driven hard through Vite, Terser, and finally Roadroller until the bundle slipped under the ceiling with only a handful of levels present. Spikes, polish, and sound effects came later, once the raw size was proven manageable.

Early stage demo

Early stage animation and tileset prototype

That philosophy shaped every subsystem. Physics were stripped heavily, portals were a DDA loop and a velocity reflection, nothing more, yet they gave the impression of continuity and momentum far richer than the implementation suggested. Animation followed the same pattern. Rather than store dozens of separate sprites, I packed horizontal strips into the atlas and sliced them at runtime, calculating offsets so trimmed frames would never “pop.” The state machine that drove those frames was skeletal, just a dictionary of keys to frame ranges, but because frame timing was tied to player state and velocity, it looked alive.

Every feature had to justify not only its play value but also its byte cost. The font system, for instance, was not an afterthought but a deliberate exercise in compression: each glyph distilled into a 35-bit integer, serialized as a seven-character base-36 string, then unpacked at draw time with a tight shifting loop. It felt closer to sleight of hand than actual engineering, saving hundreds of bytes, and Roadroller rewarded that regularity by squeezing it down even further. Levels passed through the same scrutiny. They began as plain JSON maps of block tiles right from the Tiled level editor, heavy but legible. Later they were transmuted into run-length-encoded base64 strings with a clever inflater. The savings were no joke at all, the space reclaimed was enough to squeeze plenty of levels into the final build.

All of this thrift was in service of a kind of polish players could see. The parallax behind the cat worked. The portals worked well. The atmosphere felt richer than the codebase had any right to. Some people said as much, and those moments of recognition were gratifying. But polish cuts two ways. It invites admiration, but it also sharpens expectations. A player impressed by the presentation was also a player quick to notice when the tutorial refused to explain its logic, or when the controls ignored their preferred keys. The engine itself was a success: compact, coherent, purpose-built. However, the very smoothness of its sheen made every crack in usability shine brighter. In the end, what was gained in compression was sometimes lost in the unpolished edges that remained visible.

Fascia demo

Late stage portal and physics prototype

Teaching the Player: Failure and Repair

The first fracture appeared at the tutorial, or rather, the lack of one. I opened with a test when I should have opened with a lesson. The very first screen presumed a chain of four insights: identify the black tile beneath your feet, fire a portal into it, notice the matching tile across the gap, place the second portal, and deduce that entry required a jump. Four unspoken rules before the player was permitted a single success. Many stopped at the first spike and sensibly concluded the game had nothing to teach them.

level 1 solved

One of the main intended routes to solve level 1

Onboarding works when mechanics are introduced one at a time. In Flykt I did the opposite. I assumed players would uncover the rules for themselves and then punished them when they failed. That was not very clever design. I made sure the levels were playtested well, just didn’t properly scale them by difficulty to introduce the mechanics gradually.

The Director’s Cut rebuilds this from the ground up. Hints are present and are state-driven. Miss a grey wall and the game highlights the viable black surfaces. Land on spikes and a clear message explains what went wrong. Succeed and the hint dissolves. The first level is fairly easy: an open pair of black tiles to experiment with freely, then a single jump without advanced movement. The dialogue between player and game is constant but quiet, always responsive to the last attempt.

help-sequence

Director’s Cut tutorial level

This rebuilt tutorial became the blueprint for the Director’s Cut as a whole. It merges community feedback with the recognition that discovery must be invited, not demanded. It finally teaches before it tests, and it is the version I will stand behind once the jam is over.

Input Assumptions

The second fracture was controls. I built Flykt mainly around a WASD plus mouse scheme so natural to me I never thought to question it. The arrow keys were added almost as scaffolding, meant for minimal accommodation, not a full doorway.

Players saw it differently. Many reached for the arrow keys first. For them, arrows were instinct. But in my code the arrows had been wired as an afterthought: they could move the cat, or they could aim the portals, but never both at once. Using them meant sacrificing half the control scheme. A player who expected to jump with arrows suddenly found the portal crosshair frozen. A player who tried to aim a portal discovered their movement inputs were swallowed. To them the game felt broken, not because the mechanic itself was flawed, but because the input model forced an impossible choice. Some called the game “unplayable,” not because the mechanic was flawed, but because my input model excluded them, or at the very least didn’t explain itself at all.

The oversight was deeper still. Jam players could have used trackpad control. What felt precise on my end was punitive, even impossible on theirs. By assuming one hardware context, I inadvertently locked out several others. Accessibility cannot live in the margins. If an input path exists, players can and will take it, and it must be complete. By treating the arrows and trackpad as secondary, I left people excluded and frustrated. That was my mistake, not theirs.

Community Feedback

The responses reflected this divide. Several praised the music and atmosphere, then stalled at the very first gap. More than one assumed the portals were broken before realizing they bound only to black tiles. The real issue wasn’t their patience, it was my design. The opening demanded too many insights at once. My pre-jam testers had been colleagues who treated confusion as a puzzle to solve. The jam audience approached as players, sampling quickly and moving on. In a competition feed, the first ninety seconds decide whether a game earns another ninety. I had built the main hook as a problematic barrier instead of a warm welcome.

The verdict on inputs was equally blunt. Mouse aim might feel crisp on a desktop, but it was punishing on a trackpad. Arrow-key players encountered partial support and concluded the game did not want them. They were correct. The comments simply described what happened on screen. That honesty was invaluable for moving the direction of the project forward into the Director’s Cut.

Lessons Carried Forward

The lessons are stark. Teach early, and teach explicitly as mechanics do not explain themselves. Do not assume which control scheme players will use. Design each as if it might be their first. Make the first level an invitation, not a filter. Treat accessibility not as an ornament but as a structure. These are obvious truths, but in the pursuit of compression tricks and systemic novelty I easily deprioritized them.

Looking Ahead

The post-jam Director’s Cut is my chance to address these concerns. Controls will be fully taught, not implied. The tutorial will unfold in sequence: placement, connection, traversal, before difficulty rises. Early levels will be playgrounds of discovery, not filters of endurance.

The engine already exists. What remains is the part I neglected: player experience. Clarity, accessibility, pacing — these will take precedence over byte shaving. The jam taught me a hard but valuable truth: a technically dense entry can still fail if its audience cannot enter. JS13k may be measured in bytes, but at least for me its deeper measurement is perspective. I leave with sharper tools, clearer priorities, and renewed respect for the players who tried to meet my game on its terms.

Closing Thoughts

I am deeply grateful to everyone who played Flykt, whether they failed to clear the first screen or pressed on to the final levels. Their reactions and insights reshaped my understanding of what this project truly was. What I received back was an education in design, communication, and technical restraint. The game ended up teaching me as much as I had hoped to teach its players.

Looking back from my first entry Z0MB13 to this year’s Flykt reveals a clear arc. Z0MB13 was technically spare but immediately legible. Players always knew what they were chasing. Flykt inverted that balance: technically dense, but often obscure, and many players left because they could not see the thread that was meant to guide them forward. That contrast is humbling, but also clarifying.

What I carry forward is not just the ambition of squeezing engines, features, audio systems, and procedural tricks into 13 kB, but the recognition that clarity must frame the challenge. Compression and technicality flourish only so far as they can help carry the player along. Next time, my focus will be on designing an experience where the technology amplifies accessibility, rather than hides it. Where ambition and approachability can share the same space.

That is the loop I intend to close, and the immediate lessons Flykt and the js13k 2025 game jam left behind for me :)

Thank you to the js13k team, community, and Andrzej for putting this jam together!


This post is licensed under GNU GPL-3.0 .
Contents