Maze Minders – Bitsy to ZZT

This post documents some work done to convert a Bitsy game to ZZT.

Bitsy is a game engine designed for making small browser-based games. Graphics are based on 16×16 rooms made of 8×8 tiles. It exports to HTML and is very accessible.


ZZT is a text-based action-adventure that enjoyed a very long after-market life, initially spurred by its built-in level editor. Boards are 60×25 in size, and are comprised of various terrain and objects that are represented by DOS text characters.


The game is Maze Minders, a short Bitsy adventure about various pixel-art stick figures wandering around an orthogonal labyrinth. This is not my best Bitsy effort — I was really just interested in drawing prancing 8×8 stick figures — but I went with it because the maps are almost entirely 8×8 solid blocks that would translate easily to ZZT’s DOS text mode.

I’m really happy to see Bitsy finding a community. It reminds me of ZZT’s following in a lot of ways, and I suppose that’s why I am doing this juxtaposition. Figuring out how to do certain things in these kinds of tools sort of becomes its own game.


Tools used

Bitsy 4.6 – 4.8 – By Adam Le Doux – Stock engine with no enhancements.
ZZT 3.2 — By Tim Sweeney – Stock engine with STK1 assets
KevEdit – By Kevin Vance and Ryan Phillips – The most common 3rd party ZZT world editor.

1 Tweaked ZZT game assets that cannot be invoked from the built-in editor.


The ZZT built-in editor and KevEdit have no undo feature at all, so it’s prudent to save your work often. Every saved copy of the ZZT game in-progress has been included in the final release — 192 saved files in all. In hindsight, I really wish that I had kept all of those in-progress snapshots of my old ZZT games.


Mapping From One To The Other

ZZT and Bitsy share similar territory, but the nuts-and-bolts behavior of both engines varies substantially. For example, Bitsy has no moving objects beyond the player avatar. All moving things are either faked by warping the avatar to different-looking rooms, or by modding the game engine.

These limitations are interesting, though. Hard limits with a low barrier to entry can lead to creative decisions.



Bitsy art is 8×8 2-color pixel tiles with a 128×128 display. Any tile can optionally be animated with two 8×8 frames, be it the player avatar, sprites, or background tiles.

ZZT is limited to MS-DOS text mode, which is typically 640×350 resolution comprised of 80×25 character cells that are 8×14 in size. 256 non-editable character symbols are available.


Bitsy supports just three on-screen colors by default: A color for the background, a color for terrain, and a color for the avatar and sprites. These colors can be anything that can be displayed in a web browser, and they can be adjusted on a per-room basis. ZZT is capable of fifteen simultaneous colors, plus black, not counting dithering. Most of those colors are highly saturated.

3rd Party MS-DOS utilities were available that could effect the following changes on a system running ZZT:

  • Apply custom fonts
  • Apply custom VGA colors
  • Disable text blinking in favor of more color combinations in text backgrounds

These utilities were rarely used.



Bitsy has no built-in audio capability, though it is possible to add with mods or by embedding an audio source into a webpage that hosts the game. ZZT provides basic control of the PC Speaker / Beeper. When run through DOSBox, this is sent to your system audio as loud square waves that occasionally crack and wobble.


Engine Limits

Being an MS-DOS game, ZZT has hard limits on what can fit into a single World file, and in many cases, no warning is provided to a developer that those limits have been breached. ZZT doesn’t seem to do much in the way of checking data integrity. This is both a blessing in terms of what is possible, and a curse when it corrupts world files. Bitsy’s limits, sky high in comparison, are less likely to be breached during typical development.


Gameplay Behavior

With no moving objects, Bitsy has no action elements. Puzzles, if present, are typically based around making the right choices, taking the right routes, or having the right items. The only built-in interface element is the dialogue box.

On the other hand, shooting and block puzzles are ZZT’s bread and butter. Although ZZT worlds certainly exist without those elements, there will always be ‘Health’, ‘Ammo’, ‘Keys’ and ‘Score’ indicators on the sidebar, along with instructions on how to shoot things.


Mapping 16×16 + 8×8 to 60×25 + 1×1

Bitsy rooms and ZZT boards are pretty different in terms of layout. What would be the best way to adapt one to the other? In hindsight, maybe this wasn’t a good project to convert.

Because of the tall dimensions of the text cells, a 16×16 map ends up being tall and distorted in ZZT. You could use multiple text cells to stand in for a given Bitsy tile (say, 2×1 ZZT cells for one 8×8 Bitsy tile), but even if you filled the entire 60×25 board with a good-looking recreation of the 16×16 original, ZZT player objects are always a single cell in size, and cannot be expanded to match the new scale.



Differences in Linking Areas Together

Travel to other areas is similar, but different enough to cause issues as well.

Let’s look at two kinds of linking: individual warp tiles that send the player to another area, and walking off the edge of the screen from one area to an adjacent location.

– Portals between areas

Bitsy allows the author to place single-tile exits that can transport the player to any room, at any possible coordinate. These exits cannot be modified during runtime, unless the game engine is modded.

ZZT has Passage cells that behave similarly to Bitsy exits. They can be destroyed, and tweaked ever-so-slightly during runtime, but placing new ones dynamically will only yield passages that point to the game’s title screen. (That’s right.) ZZT passages can be vulnerable to some esoteric problems, and they typically count towards a room’s limited tally of active objects.

Mutually-linked Passages in the first couple of screens of Town of ZZT.

The subject of ‘esoteric problems’ in ZZT is just too large for a single blog post, but consider this example. In Bitsy, you can place exit A and exit B next to each other just fine. No issues. If you put ZZT Passage A and B next to each other, you risk the possibility that a Passage may be erased from the board if the player simply walks a single cell’s distance from A to B. Best case scenario: this is a minor visual problem, if A and B both point to the same destination board. Worst case: A and B pointed to different boards, and now your player loses access to an area and needs to either load from a save file or restart.

(This effect could be used intentionally, but I’m getting way off topic at this point, so onto the next item.)


– Linking areas by their edges

ZZT provides built-in support for linking the edges of one board to another. Let’s say you have two areas that are supposed to be adjacent to each other, and you want the player to be able to travel between them by walking to the bordering edges of those areas. In ZZT, you just define board edge exits: set Board A’s Eastern edge to point to Board B, and Board B’s Western edge to point to Board A. ZZT checks to ensure that the destination cell on the other board is not occupied, so the player can’t get trapped in a wall.

To do the same in Bitsy (as of 4.8), you would need to define an exit for every cell on the edge of both rooms — either that, or just place a single exit and indicate it as such by blocking other parts of the edge, or pointing it out to the user in some way. The former is prone to manual error. The latter is valid, but sometimes you just want to have a wide open area to travel across.


An array of room exits in Bitsy, half-finished.


Board edge linking in ZZT’s Demonstration file.


In general, Bitsy exits are no-frills and instantanous — you can hide exits anywhere you want, and build up animations out of multiple rooms linked together. In contrast, ZZT board transitions are accompanied by a flashing purple screen transition.



I have not gotten too deep into Bitsy scripting, so I can’t make a call on this one. I will say that both Bitsy and ZZT have, at minimum, basic support for setting flags that can be used to tailor how NPCs respond to the user. Bitsy sprites don’t budge an inch from their designated coordinates, while ZZT objects have a small set of flow control and movement statements that can be used, abused and layered in various ways.



After trying a few ways of scaling up the Bitsy rooms to ZZT boards, I decided to just map one Bitsy tile to one ZZT text cell, and fill in the remaining board space with decorative wallpaper. This shortens the amount of time it takes to navigate through the maze, and prevents alignment issues when a sprite is supposed to block the player’s movement through a corridor.

I initially tried making the rooms fill a larger percentage of board space, and making the characters multi-cell text mode doodles, but I couldn’t really fit it into the space properly. The player also looked tiny compared to the NPCs.

Bitsy room for reference.


The room dimensions I ended up working with.


Using double-width blocks. It looks nicer, but takes longer to travel through, and introduces new issues wherever NPCs are supposed to block the player from the North or South.


An unflattering test of expanding the intro room to the whole board.


I briefly tested displaying the NPC sprite art in the corner of the screen using an array of objects, but getting this to work would require either a significant amount of typing labour, or some kind of preprocessing scripting that I didn’t have the time to work out. Also, when a dialogue box is open, there is not enough space around the edges of the screen to display the sprite in its entirety, even if it was moved to all the way into the corner.

This array of objects would have consumed about 20% of each board’s object capacity, which could cause problems when trying to fix other issues, so it definitely didn’t seem worth the effort.


Testing having a ‘Monitor’ in the corner of the screen to represent the NPC you are in contact with.


The monitor would not have been visible during dialogue scrolls, so I just embedded those graphics into the text scrolls themselves.


As mentioned earlier, DOS utilities can apply new fonts and colors at the BIOS level. Here is a quick mockup of what might be doable using additional DOS utilities:

A quick count suggests that there would be enough room to hold all of the in-game sprite art within unused symbols on the code page. However, the characters are  small and difficult to see, and all of their animation would have to be managed with ZZT scripting.

In any case, all NPCs in the final version are single #char 2 smileys. There is a visual indication when an NPC has nothing more to say — they will change over to a hollow #char 1 smiley.


Development Snippets

Converting dialogue.

@Maze Minder is the object’s name. This appears at the top of text boxes invoked by the object.
#cycle 1 makes the object process on every tick (about 10 frames per second).
#end halts program execution.
:touch is a built-in label. If the player walks into an object, the object will move execution to the topmost :touch label in its program, unless none exist.
#zap touch turns the topmost :touch label into a comment, so that you can talk to an object and get different behavior from them.


Left: Sometimes the ZZT player will damage floor graphics when exiting a passage.
Right: To make this less noticeable, you can place black empty cells around all passages on a given board, so that they at least look consistent.


In the Bitsy version, this area is entered from one of the four sides, and can only be exited by jumping down the hole in the center. In ZZT, I placed hidden objects to destroy the passages that the player uses to enter this board. These objects use #put <dir> blue normal to replace the passages with blue dithered walls. I’m not sure why, but the commands didn’t seem to work fully, only destroying 50% of the passages. I had to repeat the command a couple of times in all of these objects to fully get rid of them. I’m sure there’s some explanation for it that’s slipping my mind.


Using KevEdit’s selection highlight to show hidden terrain / objects necessary to prevent the player from wandering out into the background / wallpaper void area.


Here is the most complicated board in the ZZT version (though it’s really not too complicated by ZZT standards). The protagonist has been wandering the maze alone for a very long time, and seems to be going around in circles. In the Bitsy version, every rightmost walkable tile contains an exit that repositions the player on the next path along the top. This isn’t doable in ZZT with just passages, unless you make every path a separate identical-looking board2. However, a bug known as player cloning can be used to accomplish this kind of warping.

2 (A passage can send the player to a different part of the same board, but it only works when traveling from upper-left to lower-right, to a passage with a matching set of colors. This board needs to move the player in the other direction.)

Cloning is possible simply because ZZT includes “player” as one of the entity types that can be invoked through scripting, along with various items and monsters, even though it’s buggy and plainly unintentional.


Four sets of clone-generating objects are used, one for each teleport point. In each case, Object A waits for the player to come in contact with it. When this happens, it sends a message to Object B, which places an empty space to the South of itself,  then a clone of the player to the South. After doing this, it sends a message to Object C, which prompts that object to wait for one tick, shoot a bullet to the East, and then place a blue solid cell to the East as well.


Because of how ZZT behaves internally, this causes the Player to teleport to the East of the newly-minted clone at the top of the area. After one tick has passed, Object C shoots the clone, destroying it, the original player left unscathed.


To make this as seamless as possible (and it’s not really: you can see the cloned player for a fraction of a second before it is destroyed), each set of objects needs to be placed in the correct order, or else the commands will require additional ticks to process. If everything is in order, then each successive object can begin processing its script as soon as the engine will allow.

It’s very interesting that this bug functions as a consistent (though convoluted and potentially fragile) way of moving the player around. This article on Museum of ZZT contains more information if you’re interested in reading about player clone behavior.





KevEdit is a wonderful tool, but do take precautions in case it crashes.



The ending board mimics a Bitsy ending text box with objects. On every ZZT board except for the title screen, the player has to be present on-screen, somewhere. I ended up sticking the player in the lower-left, hoping it would be minimally intrusive if I included some objects around it to prevent movement.


An object to the North of the player always places a blue fake wall to the South, pushing the player downwards.

An object to the East is supposed to place blue fakes to the West, but I forgot that because of a programming error in ZZT, this never works when the destination coordinate is on the last row of the board. The player is free to wiggle around in the corner while the ending text rattles off.

Oh well.

The final conversion can be found on


Android Cellular Data Minimalism Strategies

Following are some notes about getting by with a limited monthly 100MB data plan with an Android smartphone. To be expanded as I get more familiar with the platform.

Rough daily data metric is (monthlyData – emergencyData) * 12 / 365, or about 2.63MB per day in my situation.

Set up billing cycle and cap information in Data Usage panel: You can configure Android to shut off Cellular data entirely when a certain threshold is reached.

Blacklist bandwidth-hungry Apps from using Cellular data in the background: also available in the Data Usage panel. Watch for any anomalies day-to-day, and blacklist misbehaving apps.

Pre-filtered web browsing: Chrome and Opera Mini can be configured to minimize image sizes via intermediary proxy servers. Opera Mini can strip out images entirely. Probably not good for any sensitive information, but OK for a quick trivia search.

Google Docs syncing is an incredible hog: Watch out for cloud syncing. Docs uses significant bandwidth to download/upload even a simple todo list.

(e 2017/Feb/13: Emergency data)

SNES Slap Bass

Don’t ask me why I did this, but some time ago I found the BRR sample for that silly slap bass instrument used in early SNES games (you know, the bwoo bwoo bwoo one), and did a pattern search on the entire archive for files containing a portion of it.

I remembered this sound as being kind of ubiquitous and inescapable on the system, but to my surprise, there weren’t as many matches as I thought there would be. 27 out of over 1500 SPC sets.

There were a few problems with my methodology:

  • The sample could be in an SPC file (in SNES sound RAM) but never actually be used by a track.
  • The sample could be modified slightly, still be recognizable as the bwoo bwoo bwoo we all love/hate, but still no direct match would be detected.
  • Any SNES soundtrack that is incompatible with the SPC dump format (ie any game that uploads new audio data mid-track) won’t be in the archive and therefore won’t be detected.

As an example: there is a slap bass sample in ActRaiser (Filmore) that sounds very similar, but didn’t count as a match. I’m not sure if it’s the same sample though. Paperboy 2 also seemed to dodge the list. Bwoo bwoo bwoo.

I could be missing significantly more, then. In any case, here is the list:

Darius Twin
Dungeon Master
Gan Gan Ganchan
Genjuu Ryodan
Hit the Ice
Hokuto no Ken 6
John Madden Football
Magic Johnson's Super Slam Dunk
Michael Jordan - Chaos in the Windy City
Might and Magic 2 (Japanese Version)
Mega Man X
PGA Tour Golf
Raiden Densetsu
Shinseiki Odysselya
Sonic Blastman
Soul Blazer
Spark World
Super E.D.F. - Earth Defense Force
Super Off Road
Super Soccer Champ
Vs. Collection
World Class Rugby

And here’s a list of matches for the individual SPC files.

(e 19/Mar/2017: Wording)

MIDI Masquerade

In 2015, someone posted a very large collection of MIDI files to Reddit, numbering at around 130,000 files total. I just downloaded and extracted it and I have now become this man.

Many of these files are transcriptions of pop, classical music, and video game soundtracks, dutifully sequenced by fans. Others are pretty awesome original works. Still others are just a single melody or set of chords.

It’s fun to pick a mid file at random, and if you like the track, to try and find out more about what it is, if it’s a cover of something, etc.

Some random picks:

  • ParadiseIsland.mid (Michael Walthius – Adventures on Paradise Island)
  • Streetlife-1.mid (The Crusaders – Street Life)
  • z2lapse.mid (LamSon Nguyen VanLam – Two Lapses)

But let me get to the main topic of this post. For whatever reason, some files that were not MIDI sequences were renamed .mid, and ended up in these internet collections. I guess people running MIDI repository sites weren’t very concerned about screening their files.

Here are some finds:

  • sou.mid – compressed archive containing what appears to be promotional content for a Japanese cop show.

  • how_do_i_live.mid – just an MP3 file. Spotted because it’s the largest file in the set.
  • horse03.mid WAV sample – Legend of Zelda: Ocarina of Time
  • hiatt.mid WAV sample – John Hiatt- Drive South
  • Amanda Wilson – Gotta Let You Go.mid – simply contains the ASCII text “file does not exist”
  • stereo.mid – WAV sample – “This is a journey into sound. A journey which along the way will bring to you new color, new dimension, new values.”
  • Somebody2.mid – dead hyperlink to an MP3 version of Somebody Help Me.

OK, so the only really intriguing one so far is the zip file with Japanese cop show stuff in it. Will add more to this post if I find anything worth highlighting.

1/5/2017 – Most standard MIDI files begin with either “MThd” or “RIFF” as a header identifier. Need to do some more culling, but a quick search for files that don’t match either header yielded around 1000 results.

19/Mar/2017 – Here are some more finds. I think I’ve satisfied my curiosity, and probably won’t come back to this. I just think it’s interesting how tiny bits and pieces of different things all over the world found their way into this collection.

  • andyl15.mid – Old 404 HTML page for the long-defunct web host

  • saluetg.mid – HTML page belonging to ‘The MIDI Farm’.
  • SUPPORT.MID – BBS listings for DMP / Dual Module Player, a module tracker program.
  • TwistAndShout2.mid – Someone’s HTML page about the Beatles’ Please Please Me, presumably a MIDI version of the album.
  • island2.mid – HTML page from “Miss Pita’s Domain”, a Geocities site circa 2000, now long gone. “Edited entirely by WebTv” is proudly displayed at the top. Here’s a mirror of the original site — though this one is a bit different.
  • hall_wee.mid – Multi-part MIME file with a couple of MIDIs based on the Halloween soundtrack (judith.mid and laurie.mid, already in the collection). Includes a promising warning at the top:
Content-Type: text/plain;
Content-Transfer-Encoding: quoted-printable

Do NOT listen to this music at midnight with all the lights off in the =
house. I did once and my friend came VERY fucking close to making me =
shit my pants.
  • F242.MID – This is a tracker module — a different sequencing format that includes instrument samples. F242 refers to German electronic group Front 242, which this module samples (per the internal notes).
  • bark.mid – NeXT Sound File of a dog barking.
  • swans.mid – NeXT Sound File, 19 second excerpt of a song I haven’t identified.
  • meg.mid – Shin Megami Tensei “BMS” music pack. Looping samples and a header file containing sequence data. My understanding is that this would be imported into a rhythm game.
  • bumble_b.mid – Seems to be a system component of an old version of Windows, maybe 3.0 or 3.1. References to EMS and various Windows file paths and fonts.
  • amc.mid – BMS package, similar to meg.mid above.
  • DAYSOF.MID – Sampled tracker module, like F242.MID above.
  • c04014.mid – Text file. Appears to be lyrics for a pop song by Taiwanese duo Ukulele – 《不知所措》 優客李林
  • JOYWRLD.mid – 
  • bluegrass.mid – 
  • absence_of_fear.mid – 
  • two_of_us.mid – 


And here’s a false positive: andshewas.mid is a valid MIDI file. Something prepended a text header to the top — it works if you delete that from the file.

(e:25/Mar/2017: wording)

Cave Noire


Konami’s 1991 Cave Noire is a bite-sized dungeon crawler for the original Game Boy. Released in Japan only, it seems to have come and gone without much notice, outside of a small following on the internet. With minimal story and randomized dungeons, it can be considered a cut-down take on the Roguelike genre, with an emphasis on evading monsters.


Cave Noire has four dungeons with 10 difficulty levels each. Each dungeon uses mostly the same creatures and chamber data, but they have unique background tilesets, and the win conditions are different.

Once a dungeon’s win conditions are met, you need to find an escape door and get out alive. Completing difficulty level 1 of a dungeon will let you play level 2, and so on, up to the tenth level (called M). You win the game by completing difficulty level 6 of every dungeon, but you can continue beyond that, up to M if you want.

You can travel down a floor by using a staircase tile, or by dropping down a pit, but you cannot return to a previous floor. The dungeons vary between short floors with one or two chambers, and larger floors with up to about 8 chambers. Monster and item information is persistent for as long as you are on a floor, so if you dropped a health potion in a previous room, it will still be there on your return. While the dungeons do have a bottom floor, it is very unlikely that you will ever get there before you die or fulfill your win conditions.


cn_quest_1Dungeon 1: Defeat Monsters

You’re given a quota of monsters to destroy. On higher levels, this quota gets pretty high, and you need to pick your battles carefully.


cn_quest_2Dungeon 2: Collect Gold

Strangely, gold has no purpose in Cave Noire outside of this one quest — there is nothing to spend it on. Gold is found in treasure chests, lying on the ground in some rooms, and is occasionally dropped by the tougher enemies when defeated. Larger batches of gold are found on lower levels of the dungeon.


cn_quest_3Dungeon 3: Collect Orbs (Goblets)

Pick up a certain number of orb / goblet items and escape the dungeon. This might be the most challenging quest, because the orbs that you collect will occupy your inventory slots, squeezing out useful spells and potions. On the ‘M’ difficulty, the required orbs will take up your entire inventory.


cn_quest_4Dungeon 4: Rescue Fairies

Locate keys in the dungeon, and then use them to free captive Fairies. Rescuing a Fairy will destroy all enemies and partially remove fog from the screen.



Cave Noire features monsters with variable sizes, such as dragons that occupy four tiles, and different movement patterns. Some of them chase the player, while others patrol vertically or horizontally, or move in a small circle, or hug the walls.

Monsters will attack when you are adjacent to them, or retaliate after you strike them first. But you can also evade monsters indefinitely, even if they are right behind you, as long as you keep moving.

loop1 loop2



Periodically, you will find yourself on a dark or foggy floor. The fog masks all terrain hazards, and enemies are harder to identify (they’re drawn as a pair of cliche “evil eyes” when on a fog tile). When you move, any fog around your eight neighboring cells will be lifted. You have to tread carefully through fog so as not to fall down a hole (travel to next floor and take 4 damage), fall into lava (instant death), or get cornered by a monster.

Some resources on the game:

Monster GIFs

To the best of my knowledge, this is every enemy in the game:

cn_enemy_stationarycn_enemy_zombie cn_enemy_spider cn_enemy_skeleton cn_enemy_knight cn_enemy_minotaur

cn_enemy_mimic cn_enemy_ghost cn_enemy_gas cn_enemy_grim_reaper cn_pointer cn_fire

cn_crab_small  cn_enemy_centipede cn_enemy_stump

cn_enemy_big_bat cn_goblins_framerate_fix cn_enemy_giant_enemy_crab

cn_serpent cyclops

 cn_enemy_hydra  cn_enemy_big_guy_recropped

cn_enemy_big_dragon_white_framerate_fix cn_enemy_dragon_dark

cn_dragon_more cn_another_dragon


(e 24/Sept/2016: wording)
(e 18/Nov/2017: wording)
(e 2/May/2018: translated manual link)