This blog post is licensed Creative Common Attribution Required v4.0.
I've previously blogged about the unexpected complications of minor features, which covers how adding a seemingly small software change can turn out to be much more difficult than anticipated. There's also a related phenomenon in the software world: complex and sophisticated pieces of software that are so robust and easy-to-use, that they create an impression of being simple. A text input field is a good example. It looks simple - just a box you type text in to, right? It's a basic thing everyone who uses computers is familiar with. Yet it includes a huge amount of hidden complexity.
This sometimes comes up with our HTML5 game engine Construct. Most content renders in to a canvas, including text. It's possible to use HTML content on top of that, but sometimes it's useful to actually have things render in to the canvas instead, so you can do things like apply WebGL effects to them, draw other content on top, and so on. So sometimes people ask us: can't you just take <some feature> from HTML, and have it render in a canvas? Even in just a simple form?
It's usually not remotely simple - and we already have a good example of this: wrapping text.
When you write text in HTML, the browser handles wrapping text for you. When the text reaches the right edge of its box, it instead moves down a line and then carries on. How hard can it be?
That's the fundamental principle behind our custom canvas text word wrap engine. The original code was written about 10 years ago. We're still making tweaks to it. We learned the hard way how it seems simple, but has endless hidden complexity. Here are a few of the less obvious issues we've run in to.
There are probably some more edge cases I forgot about. That's just a few. Still think wrapping text is simple?
We learned all that the hard way, by writing our own word wrap engine for canvas and having it battle-tested in the real world in production software by lots of users. In the end our implementation covers most, but not all, of the above - some problems are just so difficult to solve it doesn't seem feasible, especially when it comes to bidirectional/vertical layout and changing formatting with joined text characters. Even with the might of a full browser engine, Chrome only fairly recently solved some of those issues with a completely rewritten layout engine named LayoutNG.
We're somewhat reluctant to go through all this again for anything else that seems simple - like a text input field. Why not make one of those too?
In this case we have not implemented a text input field in the canvas. For Construct we still create an input HTML element and place it on top of the canvas. I think this is wise, as it is another area where there is a lot of hidden complexity. If we did our own implementation I know we'd need to cover:
Windows + .
In short it's not worth the trouble to make a canvas equivalent. Using HTML for a text input may have its limitations on how it integrates with a canvas, but replacing it is an enormous engineering challenge. Some might say "but I only need a few of those things". Even that is a challenge, with lots of gotchas along the way, as we discovered with wrapping text. And then if you have lots of users using your software in different ways, they'll all want to use a different small subset. So to cover everyone, you'll probably need a near-enough complete implementation in the end anyway.
This comes up in other cases too. For example one suggestion for Construct involved a "simple" version of a flexbox layout engine. I am sure similar lists exist for how to design even a straightforward-sounding layout engine. Layout is really hard, and browsers do it well. It's far better to use the existing browser layout engine if at all possible.
If the browser - or in other environments, the operating system - can do something for you, chances are you definitely want to let it handle that for you. Otherwise you'll learn the hard way just how much work goes in to it. We did it for word wrap as displaying text is so important, but even then, it's not a complete implementation and has its limitations. At least we allow using custom HTML elements so you have a way to just use the browser's layout engine for text, with full support for every single gnarly edge case in text layout. Apparently there's a Canvas formatted text spec proposal which looks like it would also let us use the browser layout engine for wrapped text in a canvas - which would be great if all browsers supported it, and none have shipped it yet.
In conclusion, it's easy to look at mature, comprehensive pieces of software engineering and think it's simple. That is because it is the pinnacle of success in software engineering to do a great deal of complicated work, and have it work so smoothly that people think it's simple. Things like text input fields achieve this level in part because they are so fundamental to computing, and get so finely crafted over decades of intensive use that they come close to perfection. Familiarity probably also lends to the illusion, as things like text are so ubiquitous in computing that everyone feels like they know what it involves. But there are endless hidden depths to the implementation of these things. And as ever they change over time too. So don't do it: don't think you can implement that. Let the browser or OS do its job. Far more work has gone in to them than you probably appreciate.
Get emailed when there are new posts!
I wrote my own word wrapping for my game and I agree it was a huge challenge to get something that works even most the time.
I thought most comments about this wa sabout taking a "snapshot" of the rendered element/layer and being able to paste it to canvas in themiddle of the other sprites.
Is this the right TLDR?
If you do text, just use HTML elements and beautify it with CSS. Not worth the headache.