2026-01-13 23:20:54
In the 13th century Cistercian monks came up with a way to show the numbers from 1 to 9999 as a single character.
The way it works is to add the lines of different characters to each other until the number is reached. So, if you want to show 161, you take the 1, the 60 and the 100 and add them together:
Same with 1312 as 1000 + 300 + 10 + 2:
Which is pretty much incredible, so I thought it would be fun to create a generator for those characters. And here it is:
And while we’re at it, why not have a Cistercian Clock ?
Open it in your browser and enter the numbers you want to generate. You can also get the source code, download it and use it offline. You can generate numerals as PNG or as SVG, click them to download the images and click the X buttons to remove them.
The generator is based on a script I wrote to generate the numerals, all available on the GitHub Repo. There are two flavours, a simple Node based one that returns SVG strings and a more advanced one that allows for in-browser PNG and SVG generation and customisation.
You can use this on the command line using:
node toCistercian.js {number}
For example `node toCistercian.js 161` results in the following SVG:
You can also use this in a browser as shown in the simple example:
The generator uses the more detailed cistercian.js version, which allows you to generate numerals in various versions and formats.
Usage is in JavaScript and a browser environment.
const converter = new Cistercian();
converter.rendernumber(1312);
This would add an `output` element to the body and render the numeral with a text representation and a button to remove it again.
You can configure it to change the look and feel and what gets rendered by calling the `configure` method. See the advanced example for that.
If you want, for example, to render the numeral inside the element with the ID `mycanvas` as SVG with a `width` of `400`, lines 10 pixels thick and in the colour `peachpuff` and without any text display or button to delete, you can do the following:
myConverter.configure({
renderer: ‘svg’,
canvas: { width: 400 },
stroke: { colour: ‘peachpuff’, width: 10 },
addtext: false,
addinteraction: false,
outputcontainer: document.getElementById(‘mycanvas’)
});
myConverter.rendernumber(1312);
As with many things I code for fun, this started offline, with me thinking how to approach this issue. In essence, all I had was an image of the numerals. When I got home, I thought I should give this to Copilot to vibe code like all the cool kids do. I asked it to take this image of numerals and create SVG versions for each of them (so I could link to them). The result was fast, immediate, confident and utter garbage.

So I went back to analysing the numerals and instead of creating them as SVGs, I created them as a dataset. In essence, these are characters on a 3 by 5 grid. I numbered the points and wrote them down as coordinates:

this.points = [
[10,10],[30,10],[50,10],
[10,30],[30,30],[50,30],
[10,50],[30,50],[50,50],
[10,60],[30,60],[50,60],
[10,80],[30,80],[50,80]
];
The middle line is never used in the real numerals, but hey, why not?
Then I looked at the numerals and noted down which points are connected for each of them. 1 and 13 are always there as this is a vertical line in the middle. This gave me the dataset to use with Canvas or generate SVG from. Here are the indices of the points array that describe all the glyphs:
this.glyphs = {
0: [[1,13]],
1: [[1,2]], 10: [[0,1]], 100: [[14,13]], 1000: [[12,13]],
2: [[4,5]], 20: [[3,4]], 200: [[10,11]], 2000: [[9,10]],
3: [[1,5]], 30: [[1,3]], 300: [[13,11]], 3000: [[13,9]],
4: [[4,2]], 40: [[4,0]], 400: [[10,14]], 4000: [[10,12]],
5: [[1,2],[2,4]], 50: [[0,1],[0,4]], 500: [[13,14],[14,10]], 5000: [[13,12],[12,10]],
6: [[2,5]], 60: [[0,3]], 600: [[14,11]], 6000: [[12,9]],
7: [[1,2],[2,5]], 70: [[0,1],[0,3]], 700: [[13,14],[14,11]], 7000: [[13,12],[12,9]],
8: [[4,5],[5,2]], 80: [[4,3],[3,0]], 800: [[10,11],[11,14]], 8000: [[12,9],[9,10]],
9: [[1,2],[2,5],[5,4]], 90: [[0,1],[0,3],[3,4]], 900: [[13,14],[14,11],[11,10]], 9000: [[13,12],[12,9],[9,10]]
};
The rest was just comparing and looping over this array.
The logic of adding to the final numeral was not too taxing either. When the number wasn’t defined in the glyphs array, I turn it into a string and loop over it from the end to the start. Each number then gets the added zeroes to allow for the lookup:
let chunks = number.toString().split(‘’).reverse();
chunks.forEach((chunk, index) => {
let value = chunk + ‘0’.repeat(index);
So, for 1312, this would become 1312 and on each loop iteration I get the data:
Feel free to check the source of the script for some more fun bits. And yes, I did use Copilot to help with some of the cruft code I didn’t feel like writing by hand, especially turning functions into methods and such.
I had fun, I hope you find it interesting, too.
2026-01-02 21:08:34
Lately, I have found an incredibly annoying pattern in social media—especially LinkedIn posts: the “you are already behind” posts, claiming that by not using product $XYZ you have already been beaten by the competition. These incendiary headlines are often followed up by a testimonial that the author used $XYZ to deliver 10-23x the amount of work he (yes, most of the time “he”) used to deliver. And, of course, the post ends with a special discount to try out product $XYZ.
I utterly despise this narrative; it is insincere, plays on people’s worries, and doesn’t actually deliver any solution. It’s plain and simply an ad for a product or a crowbar approach to paint the original poster as a thought leader who knows what the future holds.
Fun fact: in my whole career, I’ve always been told that I am behind the pack for not embracing certain products or technologies, and many passed me by without affecting my career or the products I built and sold at all.
And yet, here I am, having had a good career and some money. I didn’t burn out and don’t really feel like I should have been one of the super early adopters failing to make an impact in the long run. I celebrate the successes of other people and fast implementers, but I also spent far too much time fixing what others innovated to work in production. There’s always a “but $PERSON uses $XYZ and delivers much faster and simpler than you do.” Well, let them. Remember that you are a professional, and you want to deliver great work, which takes time, effort, and quality thinking. So, demand that from your customers and especially from yourself.
So here’s my advice: don’t interact with posts like these, avoid people who post these, and instead find your own pace and peace of mind. You will be most effective when you are happy in achieving what you want to, not what a made-up market or competition demands from you. If I am already behind the people running against a wall, that’s actually a good thing.
2025-12-26 20:17:28
I’ve stated on several occasions that Lego made me a developer. I was the youngest of four kids who inherited a huge box of bricks with no instruction booklets. So I took lots of smaller bits to build bigger things and re-used skills and ways to connect things. I came up with my own models just to dismantle them and re-arrange things.
Much like you write software:
Now, this December my partner got me a Blue Brixx advent calendar with Peanuts characters that can be Christmas ornaments. It taught me that Blue Brixx is much more like current software development.
Lego is great to assemble and sometimes tricky to detach. But it is always possible.
Don’t tell me you are a child of the 80s if you haven’t at least chipped one tooth trying to separate some stubborn 4×2 Lego bricks.
With Lego you get instructions that show you each step of the way which parts are necessary. It’s a bit like following a tutorial on how to develop a software solution.
With Lego, you have all the necessary bricks and none should be left over. Much like with IKEA, any time you have to use force, you’re doing something wrong and it will hurt you further down the track.
Blue Brixx, because of its size, make and price, is different. The models are adorable and fun to build, but you need to prepare a different approach.
Which is a bit like software development these days. We use libraries, frameworks, packages and custom-made, reusable solutions. Often we find ourselves assembling a Frankenstein solution that is hard to maintain, tough to debug, has horrible performance and gobbles up memory.
Just because we re-used bricks we’re not quite sure if we put them together the right way. And we sometimes have to use force to make them work together in the form of converters and optimisers. We add tons of bricks upfront that are loosely connected and lack structural integrity, so we add even more tools to then analyse what’s shipped but isn’t needed and remove it again. We don’t have a manual to follow and we look at the shiny end results built with a certain library and want to take a shortcut there.
I’ve seen far too many products that used several libraries because one component of each was great, resulting in a bloated mess nobody understands.
This is exacerbated by vibe coding. The idea is never to care about the code, but only about the solution, and that will always result in starting from scratch rather than maintaining and changing a product. Think of this as Lego models you glued together.
OK, the first thing I realised is that I need new glasses. I already have varifocals, but my eyesight must have declined – spoiler: it did in 3 years. I either can check the instruction booklet with the surprise brick illustrations or find the correct one without my glasses or I need the glasses to find the small brick on the table. This is frustrating, not to even mention the ergonomics of the situation resulting in a hurting back.
Until my new glasses arrive I am using a LED panel lamp I normally use for my podcasts to give the bricks more contrast and see much more detail.
If that is not enough I use my mobile phone as a magnifier to analyse the booklet.
And last but not least I started to pre-sort the bricks of each model before assembling it. This gives me weird looks by my partner of the “what a nerd” variety, but it really helps.
This is also how I build software and try to find my way in this “modern” world of cutting straight to the final product:
Building these things is work, but it also gives me joy to have assembled them by hand. I also learn a lot how certain parts are always achieved in the same way (hair, arms, legs, parcels…) and It gets easier the more I do it.
I doubt that I would feel the same fulfilment if I asked ChatGPT to build me a 3D model and print the thing.
2025-12-17 19:05:31
The other day I found out that you can watch YouTube in Albania without ads.
Personally I pay for YouTube and I think it is worth while, but I found that curious. Reasons might be that Google has no advertisement contracts in the country or it may just be too small a market to matter to them. Regardless, whenever you post something like that there will always be an army of smug people telling you that any service is ad free to them as they use an ad blocker, or other means not to show ads.
The experience for users who don’t employ these means is different, though, to the extend that the web is becoming unusable. I also do lament that on social media:
Here’s the thing: I am 100% sure these things are connected. The more people block ads, the more aggressive advertising became. To the extend that a newspaper site of today reminds you of illegal download or porn sites of the early 2000s. This is not a matter of greed, but survival, as subscriptions don’t scale. I pay for a few newspapers, but I really can’t be bothered to subscribe to all that I want to read. The internet replaced paper, after all, and it should also have replaced the means of acquiring news and content.
People claim that using ad blockers is about privacy. It can be, but most people use this as a hypocritical defense. Often these are the same people that don’t have working SSL on their sites or plaster them with third party “like” buttons. Or log in with Facebook/Google/Microsoft into other services. Pick a lane!
I am a privacy advocate and it is important to defend ourselves from being tracked online. “I don’t have anything to hide, so I don’t care when people know what I do” only works until your actions become accidentally – or by design – part of a bad narrative.
But there is a difference between tracking prevention and blocking ads. Not every ad is predatory and designed to record your actions – yet. Tracking prevention is often baked into the OS - at least in Europe – or there are specialist browser extensions like Privacy Badger. By using a dedicated “Ad” blocker, what you do is trying to get access without paying. That feels like doing a clever thing, but it hurts the web as not many things are free.
When I started on the web, I was a radio journalist. I announced the news and I got faxes and later on emails every morning with what happened in the world and our local area. We paid for getting access to these services, so our radio station played ads to cover these costs and my salary.
When I got internet access at the radio station (around 1995), I got access to the news tickers of government and international news agencies. I got the news faster, less filtered and for free, as companies and service providers published the information for free to be part of the cool new thing called internet. This also involved excellent innovations like RSS. The BBC was great at that and Netscape even had a sidebar displaying news. The platform “web” doesn’t charge you for publishing – it is an open platform. That was a huge step forward in publishing, and I quit my job and started developing and writing for the open web.
Hosting isn’t free, but it was affordable to companies and many people published on platforms that had ads on them – Geocities and the like. Even back then there were “framebusting” scripts that would hide the ads, effectively violating the terms and conditions to publish there.
The big mistake we made is to treat the web like any other publishing platform. We cherished the reach to get to a 24/7 world-wide audience, but our genius idea of making money was to show ads. That worked in classic media, so why not here?
I remember that at the radio station this got ridiculous at times when – for example – someone sponsored the announcement of the time. I had to play a 20 second jingle and then tell the listeners that it is 9 o’clock followed by another 4 second jingle that this amazing piece of news was brought to you by company XYZ. These days, YouTube has similar things where a 10 second video has a long non-skippable ad beforehand.
This is what I hated about traditional television, too. Watching Mythbusters on a streaming service is fun. Watching it on tele with “after the break” and “before the break” vignettes repeating what I just saw is grating.
On the web, we distribute content and leave it to the user to make it consumable to them. We have to support mobiles, desktops, voice interfaces and many other things. We don’t distribute payment though. Instead, we show ads and hope people click them. Or we ask for people to subscribe to one service at a time. There were some efforts to allow for easier payment, like Flattr, but they never made it to the mainstream or got baked into an operating system. This is a shame, as I would love to say I pay a lump sum each month and distribute it to publishers once I consumed the content. I fail to see why I should pay upfront to get access. A physical paper I also skim at the store before purchasing it to see if it is worth it.
When Google came around the whole ad thing exploded. This blog had banners at a time and I did make about 2K a month with them. I vetted the providers and made sure that relevant stuff was shown. But I also found that there were dozens of blogs that scraped my content and drowned it in ads, making more money than I did. Linkfarming, Black Hat SEO stuff was mushrooming and it all had the goal to show ads around content that wasn’t made by the people who tried to benefit from it. So, naturally, people were annoyed and started using Adblockers.
I very much realised that, too. My banner income went towards zero, so I just took them off – no point in causing more traffic that nobody benefits from. Other people got hit much harder. Publications like Smashing Magazine had to reconsider from scratch how to pay their writers.
But then something really messed up happened. Google itself turned from a place to get your stuff found to a place that gives you 90% ads and sponsored content, with the first web content showing up on page 4. I worked at Yahoo when Google really became a thing and our business model – buy content from news agencies and show ads around it – became the standard model. The more people blocked ads, the more needed to be shown to those who don’t.
So this is where we are now. Web search is a mess and becomes more or less useless. And companies stop publishing on the open web because there is no way to be found. Instead, we are pushed into closed environments that promise to use Artificial Intelligence to give us only what we really look for, until the money runs out. Even now ChatGPT and others consider displaying ad content which can’t be blocked.
So, yes, I do understand people who use Adblockers, but it also feels like finding and open fire escape at the back of the concert building. Sure, feel smug and clever getting in, but you’d better go and buy some merch to support the artist.
2025-11-24 16:27:47
In his excellent talk Get the Core Right and the Resilient Code Will Follow at Beyond Tellerrand in Berlin this year, Andy Bell showed how to sensibly discuss a coding issue amongst your team. He also did a more in-depth write-up on his blog.
The problem that Andy described was having a CSS grid with columns of content next to another and then being asked by the client to randomise the order of the grid on every reload. So that each part of the company will get its 3 seconds in the limelight. Andy is a strong believer in resilient code and rightfully is critical of JavaScript solutions for issues like this. Moving a whole DOM construct like this around can be slow and writing out the whole grid as a component on each interaction can also be iffy.
So I pondered what to do and remembered the CSS order property applying to flex and grid items. Using this one, you can re-order items visually.
Excellent bands you never heard about
If you change the order of the third element to minus one, it shows up first:
ul {
display: flex;
flex-direction: column;
gap: 1em;
}
#dk {
order: -1;
}
This keeps all the re-ordering work in the CSS engine. But as you can’t as yet have random ordering in CSS, we need some JavaScript to create that functionality. The first idea was to add the order as inline styles, but that would be the same DOM manipulation and having to loop through all items. Instead, I thought about using CSS custom properties:
ul {—customorder: -1;
display: flex;
flex-direction: column;
}
#dk {
order: var(—customorder);
}
That way I can access `customorder` on the parent element:
let ul = document.querySelector(‘ul’);
ul.style.setProperty(‘—customorder’,-1);
Putting all of this together, I give you GridShuffle and you can check the code on GitHub:
About
This is the order.html file.
News
Latest news will be displayed here.
Contact
Contact information will be displayed here.
Case Studies
Case studies will be displayed here.
The grid above shuffles around on every reload or when you active `shuffle` button. There are many ways to achieve this effect, but this example uses only CSS properties to set the order of the grid items. The HTML is not altered and there is no DOM manipulation other than accessing the CSS properties of the parent element. This should make this highly performant. The JavaScript code rotates the order of the items in the grid by changing the CSS variables that define their order. Below is the relevant code used to set up the grid and shuffle the items:
#contentgrid {—items: 4;—item1-order: 1;—item2-order: 1;—item3-order: 1;—item4-order: 1;
display: grid;
gap: 20px;
grid-template-columns: repeat(
auto-fit, minmax(200px, 1fr)
);
}
#about { order: var(—item1-order); }
#news { order: var(—item2-order); }
#contact { order: var(—item3-order); }
#casestudies { order: var(—item4-order); }
We define the order of each element as `1`, which means that if we set any of them to `0`, it will be displayed first in the grid. For example, if we did the following, the `Case Studies` section would be displayed first:
#contentgrid {—items: 4;—item1-order: 1;—item2-order: 1;—item3-order: 1;—item4-order: 0;
/* … */
}
#about { order: var(—item1-order); }
#news { order: var(—item2-order); }
#contact { order: var(—item3-order); }
#casestudies { order: var(—item4-order); }
This could be done on the server-side or with JavaScript as follows:
let root = document.querySelector(‘#contentgrid’);
let itemcount = getComputedStyle(root).
getPropertyValue(‘—items’);
let old = 1;
const shuffleorder = () => {
let random = Math.floor(Math.random() * itemcount) + 1;
root.style.setProperty(‘—item’ + old + ‘-order’, 1);
root.style.setProperty(‘—item’ + random + ‘-order’, 0);
old = random;
};
shuffleorder();
We get the amount of items in the grid by reading the value of `—items` CSS property on the root element, and store the current first one in the `old` variable. We then pick a random number between `1` and the total number of items, and set the order of the old item to `1` and the new, random, item to `0`. We then re-define `old` as the new item.
This is the most basic way of doing this, and it is not a real shuffle, as it only rotates the items in a fixed order. You can see the Shuffle Grid example for a more advanced implementation as it randomised the array of all the items. You can also rotate the items by shifting the array of all orders as shown in the Rotate Grid example.
The other examples also don’t need any of the custom properties to be set, but create them instead. This means a tad more JS + DOM interaction but makes the process easier.
let root = document.querySelector(‘#contentgrid’);
let cssvar = `—item$x-order`;
// Get the amount of items
let elms = root.querySelectorAll(
root.firstElementChild.tagName
);
all = elms.length;
// Initialize the order array and
// set the order on the items
let orders = [];
for (let i = 1; i
orders.push(i);
elms[i – 1].style.setProperty(
‘order’, ‘var(’ + cssvar.replace(‘$x’, i) + ‘)’
);
root.style.setProperty(cssvar.replace(‘$x’, i), i);
}
But what if you wanted to not use any JavaScript at all to achieve this? Andy’s solution he showed during the talk was to randomise the order server-side, which is easy to do in many languages. His solution using Jekyll was to generate the page every few minutes, using the sample filter which seems a bit wasteful, but is stable.
Currently there is no way to randomise or shuffle items using only CSS. However, there are solutions involving Sass which requires pre-processing. Another nifty solution is this way but it requires the use of `@property` which is not widely supported yet.
The CSS spec defines a random() functionality that could be used to achieve this effect without JavaScript. However, as of now, only Safari Technology Preview 170 supports this feature.
Polyfilling CSS isn’t easy, so it might make sense for now to add a dash of JavaScript to your solution to achieve effects like this. This way seems to me a good compromise as it keeps the functionality in CSS instead of shuffling around whole DOM trees or re-writing the whole grid.