LambdaHack is a Haskell roguelike engine where content is read-only data and the engine is code
Haskell game engine library for roguelike dungeon crawlers; please offer feedback, e.g., after trying out the sample game with the web frontend at
At a glance
- What is it?
- LambdaHack is a Haskell game engine library for ASCII roguelike games, bundled with a sample dungeon crawler playable in the browser. Its central design claim is a type-enforced separation between engine code and read-only content, and that separation is what you inherit when you build on it.
- Who is it for?
- LambdaHack fits a Haskell programmer who wants to write a procedurally generated roguelike with a real engine underneath, and who is prepared to treat world content as data that the engine validates at compile time. It does not fit a team that wants to ship a browser game quickly, since the project warns that the sample runs slowly in the browser, is limited to the square font, and can corrupt savefiles when closed mid-save.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Haskell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
An engine library shipped together with a sample dungeon crawler
LambdaHack is a Haskell game engine library for ASCII roguelike games of arbitrary theme, size and complexity, with optional tactical squad combat. That is the short description. The longer one is that the engine and a sample dungeon crawler in a quirky fantasy setting are packaged together, and the sample game can be tried out in the browser at the project homepage.
So there are two things in one repository, and the distinction matters for how you use it. There is the engine, which is the library other people build on. There is also a complete game, which serves as the reference for what the engine can do.
The sample is not a throwaway demo. The project documents a run through it in detail, including a scenario where the party shoots down explosive projectiles that would otherwise hit them, in a semi-automatic stealthy speedrun of the escape scenario using a native binary, the SDL2 frontend, and a single tiny bitmap font. The enemy gang has a huge numerical and equipment superiority, and the run works because hero 1 draws enemies and unfortunately enemy fire as well, which is when the projectiles are shot down, and heroine 2 then sneaks behind enemy lines to reach the remaining treasure.
For someone evaluating an engine, that narrative is more informative than a feature list, because it shows the engine modelling projectiles, friendly fire, line of sight, and inventory in the same simulation.
Content is data, engine code is code, and the type system enforces the split
The engine's central design idea is stated plainly: the fundamental source of flexibility lies in the strict, type-enforced separation of engine code from the read-only content, and of clients, human and AI controlled, from the server.
How you use the engine follows from that. You specify the content to be procedurally generated. You declare what the game world is made of, meaning entities, their relations, physics and lore, and the engine builds the world and runs it. The library lets you compile a ready-to-play game binary using either the supplied main loop or a custom-made one.
Two properties of the Haskell type system make this work in a way that would be hard to copy elsewhere. The separation between clients and server is enforced by types, so a client cannot accidentally reach into server state. And the separation between engine and content is enforced too, so content that violates the engine's assumptions fails to typecheck rather than failing at runtime deep inside a level generator.
The repository layout backs this up. There are separate directories for the engine side, engine-src, and for content, definition-src, plus a GameDefinition directory that holds the shipped game's definition and its default UI config, a ts-src directory, a test directory, and a tools directory.
Two frontends, SDL2 by default and a WebAssembly browser build
A couple of frontends are available. SDL2 is the default for desktop, and there is a WebAssembly browser frontend. Many other generic engine components are easily overridden.
The browser frontend is the reason the sample game can be tried out at the project homepage, and it is also the source of the most practical caveats in the project text. The game runs rather slowly in the browser, fastest on Chrome, and you are limited to the square font for all purposes, though it is scalable. Savefiles are prone to corruption on the browser, for example when it is closed while the game is still saving progress, which takes a long time.
The project's own advice follows from that: after trying out the game, you may prefer to use a native binary for your architecture, if one exists.
That is a fair description of the state of browser deployment for a Haskell roguelike engine. The wasm path works, but it is the demo path, not the path to recommend to a player.
Pre-compiled binaries need SDL2 on Linux and carry their own libraries on Windows
Pre-compiled game binaries are available through the release page. To use a pre-compiled binary archive, you unpack it and run the executable in the unpacked directory, or use program shortcuts from the installer, if available.
On Linux you need the SDL2 libraries installed on your system. The project names the Ubuntu packages: libsdl2-2.0-0 and libsdl2-ttf-2.0-0. For Windows, with XP no longer supported, the SDL2 and all other needed libraries are included in the game's binary archive. So the Windows route is the self-contained one and the Linux route is the one where a missing shared library is the first thing you hit.
The repository also carries a Windows installer script, .windows.installer.nsi, along with a Makefile whose targets drive the game from a source build rather than a binary. The play target runs the game binary with debug message serialisation, log priority 4, a save prefix of play, and initial RNG dumping, which is the kind of instrumentation you want while developing rather than while playing.
The Makefile exposes other modes of the same engine through the same binary, which shows how much of the engine is data-driven. The lore targets set a game mode and expose places, items, and actors:
play:
$$(cabal list-bin exe:LambdaHack) --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngsThe separate lore targets use the crawl, dig, see, and short game modes with knowItems, exposePlaces, exposeItems, exposeActors, showItemSamples, benchmark, noAnim, and maxFps 1000, all against a save prefix of know. There are also configure-debug and configure-prof targets that turn on profiling with different detail levels, the debug one with expensive assertions and optimization disabled.
Configuration lives in a file that gets rewritten when it is missing
The game UI is configured through a config file, and the default config settings, the same ones built into the binary, live in the repository at GameDefinition/config.ui.default.
The lifecycle is worth knowing because it can surprise you. When the game is run for the first time, or whenever the config file is deleted, the file is written to the default user data location. That is ~/.LambdaHack/ on Linux, a path under C:\Users\<username>\AppData\Roaming\LambdaHack\ on Windows, and Inspect/Application/Local Storage under the RMB menu when run inside the Chrome browser. If the user config file is outdated or corrupted, it is automatically moved away together with old savefiles, and at the next game start the new default config file appears in its place.
Font and window settings are the main reason people open that file. The default bitmap font 16x16xw.bdf used for the game map covers most national characters in the Latin alphabet, so you can give custom names to player characters, and results in a game window of exactly 720p HD dimensions. The 8x8xb.fnt bitmap font results in a tiny window and covers Latin-1 characters only.
For high resolution displays, or if fullscreen mode is requested in the configuration file, allFontsScale needs to be set, and scale 3 works for 4K displays. The chosenFontset option governs not only the main game map font but also the shape of the rectangular fonts, if any, in which longer texts are overlaid over the map.
Forking is allowed, but the licence combination decides what you ship
The engine and the LambdaHack sample game are bundled together in a single Hackage package released under the permissive BSD3 license. You are welcome to create your own games by forking and modifying the single package.
The project asks for something in return, and the reason is technical rather than sentimental. If you fork, please consider eventually splitting your changes into a separate content-heavy package that depends on the upstream engine library, so that improvements to the common codebase can be shared. The alternative route is to start separate from the outset by cloning and rewriting Allure of the Stars, mixing and merging with the sample LambdaHack game rules at will.
Then there is the licence trap, and it is spelled out. The LambdaHack sample game derives from the Hack and Nethack visual and narrative tradition, while Allure of the Stars uses the more free-form Moria and Angband style. Allure of the Stars also uses the AGPL license, and BSD3 plus AGPL equals AGPL, so make sure you want to liberate your code and content to such an extent.
That sentence decides the licensing question for most commercial projects. Fork the BSD3 sample and the result is BSD3. Fork the AGPL game and your derivative work is AGPL. The repository also carries a separate COPYLEFT file and a CREDITS file, which is a hint that the attribution side of this history is not trivial either.
Recent commits, old tags, and a public roadmap that is still mostly a wish
The maintenance picture has two halves that do not match. The last push to the repository was on 2026-09-14, so the code is being worked on. The tagged releases are much older: v0.11.0.1 on 2023-10-30, v0.11.0.0 on 2021-12-17, and v0.10.3.0 on 2021-10-29. So there is recent development without a new release tag since 2023.
The project points you at the changelog file for recent improvements and the issue tracker for short-term plans, which is the honest arrangement when the release cadence is irregular.
The long term goals are worth reading as intentions rather than a schedule. They include multiplayer tactical squad combat, in-game content creation, auto-balancing, and persistent content modification based on player behaviour. Multiplayer in particular is the headline item, and the client and server separation described earlier is the architectural groundwork for it rather than a delivered feature.
One structural detail signals that the type-enforced separation is not incidental. The repository contains a separate cabal.project.local.js file, alongside development, stacktrace, and testing variants, and a stack.yaml, a cabal.haskell-ci file, and a LambdaHack.cabal.flattened. Keeping separate project files per build flavour is what lets the same engine be compiled for desktop, for the wasm browser frontend, and for test runs without the definitions bleeding into one another.
There is also a .hlint.yaml and a .stylish-haskell.yaml, so the code is checked with hlint and formatted with stylish-haskell as part of the normal workflow.
Editorial conclusion
LambdaHack fits a Haskell programmer who wants to write a procedurally generated roguelike with a real engine underneath, and who is prepared to treat world content as data that the engine validates at compile time. It does not fit a team that wants to ship a browser game quickly, since the project warns that the sample runs slowly in the browser, is limited to the square font, and can corrupt savefiles when closed mid-save. Before building on it, read the licensing note about forking the single package: Allure of the Stars uses the AGPL licence, and the BSD3 plus AGPL combination means your game inherits AGPL.
Frequently asked questions
What is LambdaHack and what does it include?
LambdaHack is a Haskell game engine library for ASCII roguelike games of arbitrary theme, size and complexity, with optional tactical squad combat. It is packaged together with a sample dungeon crawler in a quirky fantasy setting that can be tried out in the browser.
How do I run the LambdaHack sample game on Linux?
Pre-compiled binaries are available through the release page; unpack the archive and run the executable in the unpacked directory. On Linux you need the SDL2 libraries installed, for example libsdl2-2.0-0 and libsdl2-ttf-2.0-0 on Ubuntu.
Where is the LambdaHack configuration file stored?
The config file is written to the default user data location when the game is first run or when the file is deleted: ~/.LambdaHack/ on Linux, under AppData/Roaming/LambdaHack on Windows, and Inspect/Application/Local Storage under the RMB menu in Chrome. An outdated or corrupted file is moved away along with old savefiles and replaced by the defaults.
What licence applies if I fork the LambdaHack sample game?
The engine and sample game are bundled in one Hackage package under the permissive BSD3 license, so a fork of that sample stays BSD3. Allure of the Stars uses the AGPL license, and the project notes that BSD3 plus AGPL equals AGPL.
Can I play the LambdaHack sample game in a browser?
Yes, there is a WebAssembly browser frontend and the sample is playable in the browser. The project warns that it runs rather slowly there, is limited to the square font, and that savefiles are prone to corruption if the browser is closed while a save is in progress.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/lambdahack-lambdahack)Was this useful?