Kartones Blog

Be the change you want to see in this world

Trying Electron for packaging web apps

I know that Electron is quite famous to package web applications into native binaries, but I hadn't decided when to use it. So after reading that version 20 had just been released, I checked the tutorial and it looked insanely easy and quick to get a simple page transformed, so I decided to give it a go.

The tutorial guides you quite well and step by step, both in building a hello world web inside Electron, and then adding Electron Forge to build the binaries. I picked up my pomodoro web app and excepting a single issue regarding the favicon, it gave me no errors. Even the notifications worked out of the box!

But being a bit stubborn myself, I wanted to generate both Linux and Windows from the same Linux host, and despite what the Electron Forge Makers doc said, I wasn't able to compile Windows binaries. Having installed the pre-requisites both mono and wine (and available from my $PATH), it didn't worked with Squirrel nor with WiX. It would only compile the Linux deb installer.

This is my makers config (almost the same as the tutorial's):

"makers": [
    "name": "@electron-forge/maker-squirrel",
    "config": {
      "name": "pomodoro"
    "name": "@electron-forge/maker-deb",
    "config": {}
    "name": "@electron-forge/maker-wix",
    "config": {
      "language": 1033,
      "manufacturer": "Kartones"

My saviour was electron-builder. Just npm-installing it and adding a new script like the following:

"scripts": {
    "make-windows": "electron-builder --ia32 --x64 -w"

Made it work at the first attempt.

I knew that Electron apps tend to weight a lot, as after all they contain a NodeJS and a Chromium browser. What I didn't expected was that the Windows installer of a < 0.5MB webpage would weight 266MB! And even less that the Linux deb installer would be a whooping 600MB!!! I'm sure there are some tweaks and options to shrink this, but the default is... a bit insane... I also want to test generating the Linux binaries using electron-builder, as maybe works better than electron forge (one binary weights less than half than the other).

So, overall, it is true that Electron is very very easy to get running and do a simple packaging, and you can even do multi-arch and cross-platform compilations from a single host (which sounds as crazy as great!), but the default options seem to make it just work for building a kind of "alpha" binary, in need of extra love.

Syndicate Map Viewer

Syndicate is, alongside Quake, one of my all-time videogame jewels, not only because the gameplay was quite innovative, but because the Cyberpunk setting, visuals and sound were amazing (and still mostly are).

As now I can potentially do more than just play it, I recalled there was a website (the Freesynd project) that had documented some of the file formats, and decided to spend some time of a vacation at least beginning to build a map viewer for the game.

My initial intention is to fully display maps, including sprites, probably using something like Phaser for some smooth rendering (and so that I get to learn it too). If I were to define milestones, they would be the following: 1. Extract tiles 2. Render map tiles 3. Extract sprites 4. Render map sprites 5. Render map using Phaser

As of today, I have achieved the first two milestones (and began preparing for the third one).

The project is available at my Github: https://github.com/Kartones/syndicate-map-viewer.

Extracting tiles

The first step was the longest because it also meant creating some foundations for the future, but at least thanks to a tool I found ("Bullfrog games RNC Utilities" by Jon Skeet & Tomasz Lis), I didn't had to implement any Run-Length Encoding (RLE) logic. After some fights with outdated documentation and a refresh on little-endianness and bit masks, I was able to extract all the tiles with all the game palettes (in-game missions use only 16 colors, but chosen from 5 different palettes):

Tile reader output screenshot

Rendering maps

Having the tiles, rendering a map sounded simple... The map format itself is quite easy, just a 2-dimensional array having a stack of tiles at each coordinate. What was more fun was rendering the tiles according to the isometric projection that the game uses. All in all, I built a map exporter that saves a full-resolution PNG file (~20MB per map) of each map it finds in the data folder, with each palette file also found at the same folder.

A sample:

MAP01 with HPAL01 screenshot

MAP01 with HPAL02 screenshot


Although not having sprites means that scenarios are missing doors and decorations (traffic lights, neon signs, video walls, etc.) apart from the obvious pedestrians and vehicles, the maps already look quite cool.

In a few maps there seems to be some unused areas, maybe there wasn't enough time to test them and were left out? I also have noticed some curious tiling patterns under what acts as main ground level. I wonder if they have some special meaning for the AI, or if they are what the minimap draws, or maybe they simply were initial guides for the level designers and where left there. That's why having a GUI to see the maps and potentially toggling on-off certain layers of tiles would be so nice...

Now on to reading, extracting and rendering sprites!

How to play Quake trilogy with modern engines

Quake is forever

To me, Quake is one of the greatest videogames of all time. It never grows old, and even recently got remastered. But for those of us who prefer more "vanilla" experiences and can detect subtle differences in engines re-done from scratch (don't get me wrong, they can be awesome too), id Software's old policy of releasing as opensource their id Tech engines meant that people have adapted the titles for modern times, while keeping their nature exactly as it was.

With this post I'll just list my top engines as of 2022 to enjoy the first 3 Quake games, on any major platform (Windows, Linux or Mac).

The original games

To use a modern engine, you still need the original game. You can find all three games at many online stores, but my personal preference is the DRM-free, launcher-free version: I, II, III.

The "Quake refresh" version (also known as "Quake re-release") includes additional maps and modes and slightly improved visuals, but in exchange you need use a different engine to play, quite good but not 100% equal to the old engine.


Engine: Instead of the vanilla clients, use vkQuake. This engine builds and improves on the original, so it is 100% compatible with any mod I've tested, and it is even able to run the new maps of the "refresh" re-release. It is only not capable (as of the time of writing this post at least) of using the new re-release 3D models.

Alternative: The DarkPlaces engine still uses the original Quake engine as the base (so physics and the like are still 100% accurate), but offers an improved visual experience, particularly if you go for the HD Ultra quality texture pack and the Quake Retexture/Remodel Project models and creature textures. Not so vanilla in the visual aspect this way, but quite impressive.

Extra #1: Arcane Dimensions is a "mission pack" with so many incredible maps with extra graphics and enemies and complex mechanics, that requires an advanced Quake engine because goes out of the original 3D engine limits in many ways.

Extra #2: Quaddicted Random Map is a small tool I built to pick random maps from the amazing Quaddicted site and play them. While not all of the maps are supported (some of the zip files are a mess), a lot of them work and is a lazy and fun way to try new maps each time you want to play.

Quake II

Engine: Yamagi Is a much-needed engine that only provides fixes and small upgrades, being one of the few Q2 engines compatible with most (if not all) mods. As Quake 2 has aged quite badly, I recommend installing also the Neural Upscale textures to make things less blurry.

Alternative: vkQuake 2 aims to be the equivalent to its Quake I counterpart, but sadly uses new DLLs that seem to collide with some mods, not being as compatible as Yamagi.

Extra: Quake 2 Generations: To me Quake II is the least good of the trilogy, and this mod allows you to play with the Quake I marine weapons and physics (or with DoomGuy or BJ Blazkowicz from Wolfenstein 3D, also each with their weapons). It unbalances the game a lot, but at the same time you feel like playing a big Quake I mission pack.

Quake III

Engine: Quake3e it's based on the alternative ioQuake3 engine, but with many improvements, making it the best choice to play Q3.

Alternative: ioQuake3 is an engine providing cross-platform and vastly improved options for playing Quake III: Arena and Quake III: Team Arena expansion. Fully compatible.

Extra: [HQQ] High Quality Quake texture pack provides improved visuals keeping the same original style.

Parsing HTML and XML in Javascript

When working with Python, Beautiful Soup is my go to library to parse HTML and XML. But for Javascript (NodeJS), I was missing an equivalent, so I decided to search and try some alternatives.

Hacker News website already has a very simple design, but sometimes you just want the very minimum, the most extremely minimalistic version, so I decided to practice with it and build a "top HN news" CLI tool that simply writes the top 30 (first page) news at the time of executing the script. Perfect for a morning coffee.

The packages settled on using to accomplish this task are the following:

  • node-fetch: Polyfill meanwhile Node 18 becomes stable to use.
  • htmlparser2: One of the two halves of the scrapping/parsing. In this case, the one in charge of generating a DOM tree from the parsed HTML/XML.
  • css-select: The other half for parsing. A selector engine to easily get DOM fragments (just using htmlparser2 is too hardcore).
  • dom-serializer: In case you need it, the opposite of htmlparser2, given a DOM fragment, renders the equivalent markup into a string.
  • BONUS: htmlparser2-without-node-native: No node native modules fork, in case you want to use it client-side.

The NodeJS code is small and not complex, but as the documentation of the libraries is quite bad (or almost non-existing), with the following lines I showcase how to fetch & parse a webpage, loop through a selectAll result set, a selectOne selector, bits of how operating with child nodes works, and how to get the data (text) of an element and an attribute (href property):

import fetch from "node-fetch";
import * as htmlParser2 from "htmlparser2";
import * as cssSelect from "css-select";

const parseHtml = async (url) => {
  const dom = htmlParser2.parseDocument(await fetch(url).then((res) => res.text()));
  for (let newsRow of cssSelect.selectAll("table tr.athing", dom)) {
    const rank = cssSelect.selectOne("span.rank", newsRow).firstChild.data;
    const titleBlock = cssSelect.selectAll("td.title", newsRow)[1].firstChild;
    console.log(`${rank} ${titleBlock.firstChild.data} ${titleBlock.attribs.href}`);


And if you want to get HTML back, it's a single line of code:

import render from "dom-serializer";

// ...


And that's pretty much it.

Plain Text Accounting

Plain Text Accounting (also known as PTA) is a term I arrived at by accident.

For controlling my finances I've used Excel files for a long time, but since I use Linux I've had to deal with OpenOffice and LibreOffice... which kind of work, but aren't great, and above all, LibreOffice Calc 7 is so painfully slow both opening itself and saving files (even if they are tiny less than 10KB ones!).

After months of frustration and trying different approaches, like separate files per year or minimizing the amount of data, I recently gave up and decided to search for "as simple as possible alternatives". I also wanted to avoid "the cloud", because I don't use Office Online nor Google Docs and won't change my mind only because of this single reason.

I tried searching for "text based accounting", and I found "plain text accounting"... Or to be more precise, ledger, a CLI and plain text-based double-entry accounting system.

ledger's documentation looks initially quite daunting, with so many options, and features, and ways of writing your finances. But I strive for simple solutions, so just reading the very basic introduction and checking some examples, I realized I can already use it in a practical way with a minimal subset of the features it provides. And then I can slowly improve my skills with the software, or simply leave as it is if serves me well enough.

Sometimes an example is both easier and enough, so let me begin with a minimal one:

2022/06/01 Miscellaneous
  Expenses:Services:ServiceA       $ 123
  Expenses:Assorted:Eating         $ 456
  Expenses:Phone                     $ 5
  * Assets:Checking

2022/06/02 Employer
  * Assets:Checking              $ 10000

; this is a sample comment

First, we have an entry line (labelled Miscellaneous), with three line items, Classified as category Expenses (and some sub-categories) and a fourth entry line that refers to Assets:Checking. That last line makes ledger know that previous amounts need to be deducted (in this case, from an initial amount of $ 0). We also have another entry (Employer), containing in this case first a line item with an amount associated with Assets:Checking and an empty Income line item, so ledger adds the amount to the checking asset instead of subtracting from it.

I am still learning the way the formatting works, so there might be even more minimalistic approaches, but this works for me.

Running the CLI tool (I like the Docker way) we get:

docker run --rm -v "$PWD":/data dcycle/ledger:1 -f /data/sample.dat register checking
22-Jun-01 Miscellaneous         Assets:Checking              $ -584       $ -584
22-Jun-02 Employer              Assets:Checking             $ 10000       $ 9416

We can see how it properly deducts expenses and adds incomes, listing all entries ordered by date, and displaying at the rightmost column the balance result. So the last entry line's rightmost column represents the final balance.

While this initially looks cumbersome compared with adding numbers and labels on GUI cells, in the end is mostly similar at a conceptual level, and I haven't had to do any manual summing or counting either. Plus I have something that is quite portable to a future different system if I ever want to, and is blazingly fast.

I moved my current month finances to the system and all numbers matched perfectly, so from now on this is what I'll use.

Previous entries