Bionic Reader
Bionic reading. The idea is simple: bold the first part of each word and your brain fills in the rest. Supposedly speeds up comprehension.Whether it works is debatable. But implementing it in Perl? That's just fun.
58 characters. Processes any text file. Outputs bolded words to your terminal.perl -040pE'$l=($l=y///c)>2?int$l*.4:1;s~.{$l}~\033[1m$&\033[0m~' file.txt
The result looks like this (bold shown in brackets):
Your eyes grab the bold prefix, your brain autocompletes the word. Magic? Snake oil? Either way, it's a neat one-liner.[Th]e [su]n [wa]s [shi]ning [bri]ghtly [as] [Ali]ce [wal]ked...
Part 1: THE SWITCHES
Three switches doing heavy lifting:perl -040pE
The -040 is the clever bit. Normally Perl reads line by line (split on newlines). Setting -0 to octal 40 makes it read word by word (split on spaces).SWITCH WHAT IT DOES ------ -------------------------------------------------- -0 Set the input record separator (what splits input) 40 Octal 40 = ASCII 32 = space character -p Read input, run code, print result (implicit loop) -E Enable modern features (like say)
Each "line" that -p processes is actually a single word.
Part 2: THE LENGTH CALCULATION
This is dense. Let's untangle it from the inside out:$l=($l=y///c)>2?int$l*.4:1
The y///c trick deserves its own explanation.PIECE WHAT IT DOES ----------- -------------------------------------------- y///c Count characters in $_ $l=y///c Store the count in $l ($l=...)>2 Is the length greater than 2? int$l*.4 Yes: take 40% of length (truncated) 1 No: just use 1 $l=... Store final result back in $l
Part 3: THE COUNTING TRICK
The y/// operator (also called tr///) does transliteration. But with empty search and replace lists plus the /c flag, it becomes a counter.y///c
When both lists are empty, every character "matches." The /c flag returns the count instead of doing replacement.y/abc/xyz/ Replace a->x, b->y, c->z y/// Replace nothing with nothing (no-op) y///c Count how many "nothings" matched (all chars)
It's the shortest way to get string length in a one-liner:
In code golf, y///c wins.y///c # Returns length of $_ length # Same thing, more readable, more typing
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
Part 4: THE TERNARY LOGIC
A word-by-word breakdown:($l=y///c)>2 ? int$l*.4 : 1
Why the threshold? Short words like "a" or "be" don't need the bionic treatment. One bold character is enough. Longer words get proportional bolding.IF the word is longer than 2 characters Bold 40% of it (rounded down) ELSE Bold just 1 character
The int() truncates. No rounding, just chop off the decimal.WORD LENGTH 40% BOLD CHARS ------ ------ ---- ---------- a 1 0.4 1 (minimum) be 2 0.8 1 (minimum) the 3 1.2 1 word 4 1.6 1 hello 5 2.0 2 reading 7 2.8 2 beautiful 9 3.6 3
Part 5: THE SUBSTITUTION
This wraps the first $l characters in ANSI bold codes:s~.{$l}~\033[1m$&\033[0m~
The .{$l} quantifier is key. It matches exactly as many characters as we calculated. Not greedy, not minimal, just that exact count.PIECE WHAT IT DOES ----------- -------------------------------------------- s~~~ Substitution (~ delimiters) .{$l} Match exactly $l characters (any char) \033[1m ANSI escape: turn bold ON $& The matched text (first $l chars) \033[0m ANSI escape: reset formatting
Part 6: ANSI ESCAPE CODES
These are terminal control sequences. \033 is the escape character (octal 33 = decimal 27 = ESC).\033[1m Bold on \033[0m Reset (all formatting off)
When your terminal sees ESC[1m, it starts rendering bold text. When it sees ESC[0m, it goes back to normal.
Other codes you might use:
The bionic reader uses just bold, but you could get creative.\033[4m Underline \033[7m Reverse video \033[31m Red text \033[32m Green text \033[1;31m Bold red
Part 7: PUTTING IT TOGETHER
Let's trace through the word "reading":The space gets included in the count (8 chars not 7) but doesn't affect the visual much. Whitespace in bold looks the same.INPUT: "reading " (note trailing space from -040) STEP 1: y///c counts 8 characters STEP 2: 8 > 2, so $l = int(8 * 0.4) = 3 STEP 3: s~.{3}~ matches "rea" STEP 4: Replacement wraps it: \033[1mrea\033[0m STEP 5: -p prints: "\033[1mrea\033[0mding " OUTPUT: "reading " with "rea" bold
Part 8: THE EXPANDED VERSION
Same logic, twenty times the characters. The one-liner wins.#!/usr/bin/env perl use qw|say|; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set word-by-word input mode # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # $/ is input record separator # Space character makes each "line" a word $/ = ' '; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Process each word # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ while (<>) { # Count characters in current word my $length = y///c; # Calculate how many chars to bold # 40% for words > 2 chars, else just 1 my $bold_count = $length > 2 ? int($length * 0.4) : 1; # Wrap first N characters in ANSI bold s~.{$bold_count}~\033[1m$&\033[0m~; # Print the modified word print; }
Part 9: VARIATIONS
Bold 50% instead of 40%:Underline instead of bold:perl -040pE'$l=($l=y///c)>2?int$l*.5:1;s~.{$l}~\033[1m$&\033[0m~' file
Colored (red) instead of bold:perl -040pE'$l=($l=y///c)>2?int$l*.4:1;s~.{$l}~\033[4m$&\033[0m~' file
HTML output instead of terminal:perl -040pE'$l=($l=y///c)>2?int$l*.4:1;s~.{$l}~\033[31m$&\033[0m~' file
That last one generates HTML you can paste anywhere.perl -040pE'$l=($l=y///c)>2?int$l*.4:1;s~.{$l}~<b>$&</b>~' file
Part 10: DOES IT ACTUALLY WORK?
Bionic reading is controversial. Some studies show improvement, others show nothing. YMMV.But as a Perl exercise? It's gold:
All packed into 58 characters.* -0 flag for custom record separators * y///c as a character counter * Nested assignment in a ternary * **Dynamic quantifiers** in regex * ANSI escape sequences * The -p implicit loop
Whether it helps you read faster is beside the point. It definitely helps you appreciate Perl's density.
_____ | | | TXT | |_____| | V .-----. / \ | PERL | \ / '-----' | V _______ | | | [BI]O | | [NI]C | |_______|
Created By: Wildcard Wizard. Copyright 2026