https://zw.swerdlow.dev Drawing with Zero-Width Characters A set of 18 unicode characters generally only seen in the Qur'an broke my assumptions about text rendering The image above is a single line of white spaces with a viewbox height of 3 pixels according to the browser. Using a set of unicode characters I can make images like that, and break other website's assumptions about where text should be rendered. Months ago I found a twitter bio that had the strange effect of a giant line coming out of one of the letters. I figured out it was based around the character _ AKA U+06D7 ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE. This character has a couple of unusual properties: * It's a zero-width character. It takes up no horizontal space, the characters after it do not move horizontally because of it. These are generally used as special formatting purposes. To turn text right to left you can use U+200F (RIGHT-TO-LEFT MARK) and U+200B (ZERO WIDTH SPACE) has famously caused programmers hell when it causes files to fail compiles for seemingly no reason. * It's a combining mark. Rather than being a standalone character, it attaches to and modifies the preceding character. Common examples include accents like e (e + U+0301 COMBINING ACUTE ACCENT), umlauts like u (u + U+0308 COMBINING DIAERESIS), and tildes like n (n + U+0303 COMBINING TILDE). The base letter and its marks form what Unicode calls a "grapheme cluster" -- what we perceive as a single character. * It's stackable. When you place multiple combining marks next to each other, they don't overwrite -- they stack vertically. Most combining marks can only be used one per base character: If I put 100 U+0301 COMBINING ACUTE ACCENT after an "e", I still just get an "e" with one accent. But with certain characters, including U+06D7 ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE, you can stack multiple instances on top of each other, creating towers After doing a review of unicode characters, I was able to find 19 characters with these traits. 16 stacking upwards, 3 stacking downwards. After looking at them all (and asking ChatGPT), I realized they are all Arabic characters used in the Qur'an, referred to as Tajweed marks. a roundedHighStop U+06EC b madda U+06E4 c meemInitial U+06D8 d dotlessHead U+06E1 e yeh U+06E7 f seen U+06DC g roundedZero U+06DF h emptyCenterHigh U+06EB i rectangularZero U+06E0 j threeDots U+06DB k noon U+06E8 l sadLamAlef U+06D6 m meemIsolated U+06E2 n lamAlef U+06D9 o jeem U+06DA p qafLamAlef U+06D7 q seen U+06E3 r lowMeem U+06ED s emptyCenter U+06EA z threeDots + emptyCenterHigh alternating a roundedHighStop U+06EC b madda U+06E4 c meemInitial U+06D8 d dotlessHead U+06E1 e yeh U+06E7 f seen U+06DC g roundedZero U+06DF h emptyCenterHigh U+06EB i rectangularZero U+06E0 j threeDots U+06DB k noon U+06E8 l sadLamAlef U+06D6 m meemIsolated U+06E2 n lamAlef U+06D9 o jeem U+06DA p qafLamAlef U+06D7 q seen U+06E3 r lowMeem U+06ED s emptyCenter U+06EA z threeDots + emptyCenterHigh alternating Combining Marks and Constructing Images/Patterns These characters stack in two independent directions: the above marks and below marks. You can mix and match any of these marks together, and they'll continue to grow vertically until you add a non-combining character to reset the stack. This allows for some interesting effects, as you can create tall towers of marks both above and below a base character. A seen + meemIsolated + threeDots B lamAlef + jeem + noon + emptyCenterHigh C below: seen + lowMeem D above + below combined As a series of unique characters, using the density of their coloring you can draw images or patterns with them using the same techniques as ASCII art. As a simple POC, I took two of the three characters that have an above and equivalent below mark that had approximately the same height -- - (seen) and - (meemIsolated/lowMeem) -- and created a simple checkerboard pattern: -------------------- Based on this, I wrote a simple program to take images, turn them into black/white bitmaps, and convert those bitmaps into stacking marks. DogDemonTree Once that worked, I wrote a new calibrator that incorporated all of the upwards marks and a new grayscale translator that lets me take any image and convert it into marks like these. Despite these strings having a length of thousands of characters and rendering to this, the dom registers their text as a series of empty spaces; these entire images are just a single line of text. Calibration worked by taking all of the symbols and lining up 50 of each of them side by side, then adjusting the amounts of them till they all had approximately the same height. If you're interested you can try the calibrator here . Behavior Across Platforms * Twitter I first noticed these characters rendering strangely on Twitter. Twitter has since patched their CSS to not let it work in all the places it used to, but it still messes with notifications recieved from users who have it in their bio. I've heard of it clipping/not clipping on lots of platforms, and I've personally seen the behavior change back and forth over time. Twitter bio with stacking marksTwitter notifications affected by stacking marksTwitter sidebar with stacking marks * GitHub GitHub lets you write these everywhere, but generally clips them before they can overflow in weird ways. The most interesting behavior I found was in the repository list. GitHub in-repo view with stacking marksGitHub repository list with stacking marks * Google these seem to be fine on Google Search Results if you can get them in there. It doesn't show up on Safari Google or Safari in general for me. Google search results with stacking marks Why this happens and why it only happens on some sites? As extremely rare characters, many fonts just ignore them. Other fonts like Verdana (the font hacker news uses) renders these characters inline/non vertically stacked. One of the unique properties about these is despite obviously taking up space, they don't modify the bounding box of the text they are attached to. While most text would be expected to expand/shape the bounding box around it, these marks are ignored by it. However it doesn't ignore them -- when overflow is set to none or hidden the marks are clipped outside of the bounding box. Try it yourself [ ] SmallMediumLarge Upload an image Output Result appears here Written by Benjamin SwerdlowThank you for reading, I really enjoyed writing this. Source code is available here @benswerd