Back

Understanding the BiDi Algorithm: How Computers Render RTL Text

What is the BiDi algorithm and how does it work?

Ahmed Nagi العربية

When we write Arabic text that contains English words or numbers, things can sometimes show up in a weird order. It’s not that the computer “understands Arabic” or “understands English” the way we do — it’s actually dealing with bytes first, then encodings like Unicode and UTF-8, and finally a rendering algorithm called BiDi.

In this post we’ll walk through the whole journey: how characters get stored in memory, how each character or symbol gets its own unique number, and then how the system decides the display order when Arabic and English end up on the same line.

1. How does text get stored inside a computer?

Computers don’t understand letters or images the way we see them. At the end of the day, it’s all just zeros and ones: 0 and 1. The smallest unit here is a bit, and a byte is made up of 8 bits.

So text inside a computer isn’t stored as “letters” — it’s stored as a sequence of bytes.

2. Where it all started: ASCII

Back in the 1960s, ASCII came along and assigned a number to every English letter and common symbol. For example:

  • The letter A is 65
  • The letter a is 97

It worked fine for English, but it was way too limited — it only covered a small set of characters and symbols, so it had nothing for languages like Arabic, Chinese, or Japanese.

3. The bigger solution: Unicode

Unicode was created to fix exactly that. The idea is simple: give every character in every language a unique number called a Code Point.

Some examples:

  • The letter أ is U+0623
  • The letter ب is U+0628
  • The emoji 😊 is U+1F60A

But Unicode alone doesn’t answer the question: how do we actually store these numbers in a file or in memory? That’s where UTF-8 comes in.

4. How does UTF-8 work?

UTF-8 is the most widely used text encoding system in the world today. Its job is to convert Unicode code points into actual bytes that can be stored and transmitted.

What makes UTF-8 great is that it’s flexible:

  • 1 byte: for most basic ASCII/English characters
  • 2 bytes: for many characters like basic Arabic letters
  • 3 or 4 bytes: for more complex characters like certain symbols and emojis

This is what makes it practical — English text stays compact, while it can still represent most of the world’s languages.

Let’s take an example. Say we want to store the text A س b in a computer. The simplified representation might look something like this:

010000010010000011011000 101100110010000001100010
Aspaceسspaceb

Notice how the Arabic character needs more than one byte, unlike basic ASCII/English characters.

Important note: The computer doesn’t store any information saying “this chunk should be read right-to-left.” It just stores bytes. The display direction on screen gets handled later — and that’s exactly where the BiDi algorithm becomes important.

5. Why do we need the BiDi algorithm?

If a sentence is entirely in Arabic, there usually isn’t a visible problem. Same goes for entirely English text. The challenge shows up when you mix directions on the same line, like:

مرحبا hello 42

Knowing the characters isn’t enough here — we need rules that decide how these characters are displayed on screen in the right order. That’s the job of the BiDi algorithm, short for Bidirectional Algorithm.

6. How does BiDi determine paragraph direction?

Unicode assigns every character a directional property called a Bidi Character Type. Some characters are:

  • R or AL: characters displayed right-to-left
  • L: characters displayed left-to-right
  • EN: European numbers
  • WS: Whitespace

The algorithm then looks for the first strong character outside any Isolates to determine the base paragraph direction (P1, P2, P3). If the first strong character is Arabic, the base direction becomes RTL.

BiDi character types

R => Arabic   L => Latin   EN => European number   WS => Whitespace

bidi_character_types

7. Embedding Levels

Once the paragraph direction is set, the algorithm assigns each segment an embedding level. The core idea is:

  • Even levels mean left-to-right display
  • Odd levels mean right-to-left display

A level doesn’t describe “what type of character this is” — it describes where the segment sits in the text structure and its display direction.

Resolving embedding levels

Levels in a nutshell:

  • Even number = LTR
  • Odd number = RTL
  • Each time we enter a segment with a different direction, the level may go up
  • The maximum level is 125

Example of a base LTR paragraph:

LTR paragraph

Here the English text starts at level 0, and when an Arabic segment appears inside it, the level may rise to 1.

And an example of a base RTL paragraph:

RTL paragraph

Here the Arabic text starts at level 1, and when an English segment appears inside it, the level may rise to 2.

8. What about spaces and punctuation?

Not every character has a clear direction. Spaces and some punctuation marks are considered neutral, and their direction gets resolved based on context.

If a space sits between two segments of the same type, it tends to take their direction. But if it’s between an Arabic segment and an English one, it may fall back to the base paragraph direction. This is why spaces sometimes end up in spots that look weird if you don’t know how BiDi works.

The two core rules here are N1 and N2: neutral characters may take on the direction of what surrounds them, or fall back to the paragraph direction if the context on either side differs.

Effect of neutral characters

9. What are Runs?

After computing the levels, the sentence gets split into groups of adjacent characters that share the same level. These groups are called Runs.

Let’s take this sentence as an example:

مرحبا hello بكم

It might get split into several consecutive Runs, some RTL and some LTR, based on the computed level for each part.

10. Reordering the Runs

Once the algorithm has computed the levels and identified the Runs, the last step is reordering for display.

The general rule goes like this:

  1. Start from the base paragraph level.
  2. Look at the segments by their levels.
  3. Even-level segments are displayed visually left-to-right.
  4. Odd-level segments are displayed visually right-to-left.
  5. When there’s nesting, processing starts from the highest levels down.

(Unicode UAX #9 — Reordering Resolved Levels)

A practical example

Say we have this text:

مرحبا hello بكم

If the base paragraph level is 1, a simplified breakdown might look like:

  1. مرحبا → level 1 → RTL
  2. space → level 1 → RTL
  3. hello → level 2 → LTR
  4. space → level 1 → RTL
  5. بكم → level 1 → RTL

After applying the reordering rules, the text appears on screen like this:

مرحبا hello بكم

A few things to notice here:

  • The English word didn’t get its internal characters reversed — it’s still LTR
  • The Arabic segments were displayed within an RTL context
  • The position of the spaces was affected by the level and surrounding context

Wrapping up

To really understand mixed Arabic-English text, it helps to think in three separate layers:

  1. Unicode: gives every character a unique number.
  2. UTF-8: defines how that number gets stored as bytes.
  3. BiDi: decides how those characters are actually displayed on screen when directions mix.

So the problem isn’t just about “storing Arabic” — it’s about the whole journey from numeric representation to correct visual rendering. Once you understand this pipeline, a lot of the weird behavior you’ve probably seen in mixed-direction text starts to make total sense.