Skip to main content

How to Show Invisible Whitespace: Spaces, Tabs and Hidden Characters

See spaces, tabs, trailing whitespace and non-breaking spaces as visible glyphs. Learn why hidden whitespace breaks YAML, Python and diffs, and how to clean it.

Published By Li Lei
#whitespace #developer-tools #python #yaml #code-quality

How to Show Invisible Whitespace: Spaces, Tabs and Hidden Characters

Most of the bytes in a source file are characters you can read. A handful are not. A space, a tab, a newline, and the occasional non-breaking space all take up room and all carry meaning, yet your editor draws them as empty pixels. That is fine right up until the moment one of them is in the wrong place. Then you get a Python error on a line that looks identical to the line above it, or a git diff that flags a change you never made, or a config value that refuses to match a string it visibly equals.

The fix is to stop guessing and look at the actual characters. The Whitespace Visualizer renders each whitespace character as a faint glyph you can read: a space becomes a middle dot ·, a tab becomes an arrow , a newline shows a pilcrow before the real line break, and a non-breaking space gets its own marker . Trailing whitespace at the end of a line is highlighted, so the spaces that pad out a diff stop hiding. Everything runs in your browser; the text you paste never leaves the page.

Why hidden whitespace breaks things

Three character classes cause almost all whitespace bugs, and each fails in its own way.

Spaces versus tabs. A tab and four spaces usually render at the same width, so two lines can look perfectly aligned while one is tab-indented and the other is space-indented. Python forbids mixing the two inside one block and raises TabError or IndentationError. Makefiles are stricter still: a recipe line must begin with a real tab, and a space-indented recipe produces the famous missing separator error.

Trailing whitespace. Spaces at the end of a line are completely invisible and completely real. They pollute git diffs, because a line registers as changed when only an unseen space shifted. They break YAML and Markdown that treat end-of-line spacing as meaningful, and they trip up shell here-documents.

Non-breaking spaces. A NO-BREAK SPACE is U+00A0. It looks identical to a normal space but is a different character. Word processors and some web pastes slip it in silently, and then a string comparison fails, a number like 1 000 will not parse, or a split on a regular space misses it entirely.

The one feature that does the work

Here is the concrete point worth remembering. The tool marks spaces, tabs, trailing whitespace, and non-breaking spaces each with their own visible glyph. That single behavior is what lets you see, in one glance, that an indent is mixed or that a sneaky NBSP is sitting where you expected a plain space. You do not run a linter, parse a stack trace, or diff two files. You read the first few glyphs of each line and the truth is right there: dots mean spaces, an arrow means a tab, means the character that has been quietly breaking your YAML or your Python parse or your string match.

The counts panel reinforces it. It tallies spaces, tabs and newlines, counts how many lines end in whitespace, and raises a mixed-indentation warning the moment at least one line is tab-indented while another is space-indented in the same file.

A worked example: the tab hiding in a space file

Say this function throws IndentationError and you cannot see why:

def total(items):
    subtotal = 0
    for item in items:
	subtotal += item.price
    return subtotal

Every line looks correctly indented. Paste it into the visualizer and the indentation column gives it away instantly:

def·total(items):¶
····subtotal·=·0¶
····for·item·in·items:¶
→subtotal·+=·item.price¶
····return·subtotal¶

Four of the five body lines start with ···· — four spaces. One line, subtotal += item.price, starts with — a single tab. That is the line Python chokes on. The mixed-indentation warning in the counts panel confirms it before you even read line by line.

The trailing-space case is just as quick. Paste a line that ends with three stray spaces and the visualizer highlights the run at the end of the line and shows hello···¶. You did not have to count columns or move your cursor to the end of the line to find it.

Cleaning it up

Spotting the problem is half the job; fixing it is the other half, and the tool does both. One click copies a version of your text with all trailing whitespace stripped — every line keeps its content but loses any trailing spaces, tabs or non-breaking spaces, and the final newline is preserved. So hello followed by three spaces comes back as just hello. Paste the cleaned text back into your editor before committing and the noisy diff disappears.

One caution: do not copy the marked preview back into your code. The dots and arrows are a visual aid, not real text. Use the "Trailing whitespace removed" copy button for the cleanup, and when the fix is converting every indent from tabs to spaces (or the reverse) across a whole file, reach for the Tabs to Spaces Converter instead, which rewrites the indentation rather than just showing it.

How I use it

I keep this open in a pinned tab whenever I am reviewing someone else's YAML or a config that came out of a corporate tool. The last time it earned its place, a deploy manifest kept failing schema validation on a key that read fine to me. I pasted the block, scanned for the arrow, and found nothing wrong with the tabs — but two of the indents showed an extra dot, one space too deep. The parser was reading a nested key as a sibling. Thirty seconds of looking at glyphs replaced what would have been ten minutes of squinting and re-typing. The value of the tool is not cleverness; it is that it shows me exactly what the parser sees, instead of what my editor chooses to draw.

When to reach for it

Reach for the visualizer whenever a file is whitespace-sensitive and something downstream disagrees with your eyes: a Python block that throws on indentation, a Makefile recipe that reports a missing separator, a YAML key that nests wrong, a git diff full of changes you did not make, or a config value that compares unequal to a string it visibly matches. In every one of those cases the cause is a character you cannot see, and the answer is the same — make it visible, read the glyph, fix the line.


Made by Toolora · Updated 2026-06-13