Translate this page

Twenty-Nine Bytes to Say Hello: Golfing “Hello, World!” in SuperHack πŸ”—

A write-up of hacker.org’s “Super Small Hello World” challenge: the solutions that worked, the ones that almost did, and the trick that finally reached 29.

Overview

Table of Contents


The challenge πŸ”—

hacker.org has a little esoteric language called SuperHack. The task sounds trivial: write a SuperHack program that prints Hello, World!.

The catch is the score. You are scored on the area of the bounding rectangle of your code, and smaller is better. The program also has to finish properly: by executing the ! (halt) instruction. Crashing with a VM exception once the text is out does not count, and neither does printing stray bytes.

The number to beat was 29.

A two-minute tour of SuperHack πŸ”—

SuperHack is a 2-D stack machine, similar in spirit to Befunge:

InstructionEffect
0-9push a digit
+ - dadd, subtract, floor-divide (d by zero gives 0)
xduplicate the top of the stack
n ^push a copy of the item n below the top (1^ copies the second)
,read input. When there is no input, it pushes 0
gpop x, pop y, push the character code at (x, y). Outside the grid it pushes 0
Ppop and print as a character (low 7 bits only)
sskip the next cell
?pop; skip the next cell if the value was 0
@push the current position (and direction) onto the call stack
$pop a position, jump there, and continue two cells further on
\mirror: turns “moving right” into “moving down”
!halt
anything elseno-op

Two consequences shape everything that follows.

  1. Characters are data. g reads the program’s own code, so the string Hello, World! can simply sit in the grid and be read back one character at a time. Most of its letters (H e l o W r) are no-ops when executed. Three are not: , pushes 0, d divides, and ! halts.
  2. @ / $ make loops, and the loop count depends on where the @s are. $ resumes two cells after the @ it returns to. That lands on the next @, which pushes itself again, so a few @s can replay the loop body many times:
    • Adjacent @@@@@ give Fibonacci counts. Five of them run the body exactly 13 times.
    • @s spaced two apart give powers of two. Four of them allow 16 passes.

Thirteen characters, thirteen passes. Keep that in mind.

Area is width times height, and 29 is prime. So a 29-cell program has to be a single row or a single column.


Solution 1: the straightforward loop (area 32) πŸ”—

Solution 1 annotated

Hello, World!%0@s!@@@@@ 01^gP1+$

The first real solution puts the payload at the front and starts execution after it:

partrole
Hello, World!data, never executed, because the PC starts at the %
%entry point
0index i = 0
@ s !halt gadget: see below
@@@@@five adjacent trampolines, so the body runs 13 times
padding (explained below)
0 1^ gpush row 0, copy i, then fetch the character in column i
Pprint it
1+i += 1
$loop

The halt gadget. The first @ puts its own position at the bottom of the call stack, and s jumps over the !. The Fibonacci chain then does its 13 passes. When the call stack finally unwinds down to that first @, $ resumes two cells after it, which is exactly the !. The program halts cleanly, and there is no end-of-string test anywhere.

The padding. With adjacent @s, returning from the last @ resumes two cells later, which skips the first cell after the chain. That cell must therefore be something harmless, so it is a space.

Result: Hello, World!, clean halt, area 32. It is correct but roomy: the payload is dead weight, and the %, the padding and the 0 together cost three cells.


Solution 2: interleaving (area 28, but not valid) πŸ”—

Solution 2 annotated

1H@e@l@l@o ,1 ^WgoPr2lsds!+$

The next idea is to stop spending cells on a separate payload and weave the string through the code. The characters of Hello, World! sit in the odd columns (1, 3, …, 25), and the code fills the even columns in between:

The problem is that 16 passes is more than 13. After printing the string, the loop keeps fetching past the end and prints $ and two NUL bytes. Then $ finds the call stack empty and the VM dies:

output:  "Hello, World!$\0\0"
message: call stack underflow

It is small, but it is not a solution. It does introduce the key idea: payload characters that happen to be useful instructions (,) do double duty as data and code.


Solution 3: go vertical (area 27, but not valid) πŸ”—

Solution 3 annotated

\
2
H
@
e
@
l
@
l
@
o
x
,
g

P
W
2
o
+
r
$
l

d

!

(One character per line. The blank lines are single spaces, which are data.)

Turning the program on its side has an unexpected benefit. In a single column, g needs the x-coordinate to be 0 and the row to be i. The payload’s , supplies that 0 for free, so the fetch shrinks to x , g: duplicate i, push 0, get. The cost is one \ at the top to turn the PC downwards.

It has the same flaw as solution 2, though. The loop overshoots, prints NUL bytes, and ends in call stack underflow. At area 27 it is the smallest program in this post, and it doesn’t count.

The lesson from solutions 2 and 3 is that stopping cleanly costs cells. Interleaving makes the loop cheap, but it gives you 16 passes when you need exactly 13.


Solution 4: pay for the exit (area 30) πŸ”—

Solution 4 annotated

\
2
H
@
e
@
l
@
l
@
o
x
,
g
 
P
W
x
o
5
r
d
l
5
d
?
!
2
+
$

Solution 4 keeps solution 3’s vertical interleave and adds an honest end-of-string test:

row(s)cellsrole
0\turn downwards
12i = 2
3, 5, 7, 9@four trampolines, up to 16 passes
11-13x , gfetch row i (the , is payload)
15Pprint
17-24x 5 d 5 dtest value floor(floor(i/5)/5) = floor(i/25) (the last d is payload)
25?if the test is 0, skip the next cell…
26!…which is the payload’s own !
27-282 +i += 2
29$loop

The index runs 2, 4, …, 26. floor(i/25) stays 0 until the very last character (i = 26), so the ? skips the halt on every pass except the last. On the last pass, execution falls onto the payload !, which is simultaneously the last character printed and the instruction that stops the program.

It is a lovely piece of reuse: the payload’s ,, d and ! all serve as code. But the test costs five cells, and the total is 30.

Why 29 looked impossible πŸ”—

At this point my AI companion tried to prove that 30 was optimal. Every approach seemed to run into the same wall:

Exhaustive machine searches of the smaller areas found nothing, and searches around solution 4’s layout at area 29 found nothing either. “30 is the floor” started to look like a theorem.

It wasn’t.


Solution 5: twenty-nine (area 29) πŸ”—

Solution 5 annotated

9@s!@@@@@Hello, WorlÀ‘1^gP1+$

The break came from rereading the judge’s source code. Two lines matter:

case 'P':
  $output .= chr($this->Pop() & 0x7F); break;
...
default:
  break;   // every unknown character is a no-op

Every payload character therefore has two encodings. The plain one may do something when executed. The c | 0x80 one is guaranteed inert. That is the “formula for the data”: store the dangerous characters as c + 128, and let P strip the high bit for free.

Only two payload characters are dangerous: d becomes Γ€ (0xE4) and ! becomes Β‘ (0xA1). With those two changed, the whole payload can be executed. That removes the obstacle in the third bullet above.

Now take solution 1 and move the payload inside the loop:

colscellsrole
09i = 9 (the column of H)
1-3@ s !halt gadget, as in solution 1
4-8@@@@@five adjacent trampolines, so the body runs exactly 13 times
9-21Hello, WorlÀ‘the payload, executed on every pass
22-271 ^ g P 1 +copy i, fetch, print, increment
28$loop

Walk through one pass:

  1. The PC lands somewhere in the @ chain and runs through the payload. H e l l o, the space and W o r l do nothing. The payload’s , pushes the 0 that g needs as its row. Γ€ and Β‘ do nothing.
  2. 1^ copies i above that 0, and g fetches column i.
  3. P prints it. For the last two characters it prints 0xE4 & 0x7F = 'd' and 0xA1 & 0x7F = '!'.
  4. 1+ advances i, and $ returns into the chain.

Compared with solution 1, three cells vanish:

32 βˆ’ 3 = 29. After the 13th pass, the call stack unwinds to the gadget and the program stops on the ! in column 3.

Output Hello, World!, no VM message, 271 cycles, area 29.

The last hurdle: getting the bytes to the judge πŸ”—

The first submission came back with got: 'Hello, WorlC$' and a score of 31. The web form had posted UTF-8, which turned each special character into two bytes (Γ€ β†’ C3 A4, Β‘ β†’ C2 A1). Masked to 7 bits, 0xC3 prints as C and 0xA4 as $.

The fix is to make the browser send Latin-1, where each of those characters is one byte. On the challenge page, I had to open the DevTools console and run:

document.querySelectorAll('form').forEach(
  f => f.acceptCharset = 'ISO-8859-1');
document.querySelector('textarea').value =
  "9@s!@@@@@Hello, Worl\u00e4\u00a11^gP1+$";

Then press the normal submit button. The judge prints Hello, World!, and the King of the Hill score is 29.


Scoreboard πŸ”—

#programareaends cleanly?
1Hello, World!%0@s!@@@@@ 01^gP1+$32βœ…
21H@e@l@l@o ,1 ^WgoPr2lsds!+$28❌ extra bytes and call stack underflow
3\2H@e@l@l@ox,g PW2o+r$ld!27❌ extra bytes and call stack underflow
4\2H@e@l@l@ox,g PWxo5rdl5d?!2+$30βœ…
59@s!@@@@@Hello, WorlÀ‘1^gP1+$29βœ…

Takeaways πŸ”—


Download my free ebook

Kaue's Way Of Life

Kaue's Way Of Life
Minimum price
Free!
Suggested price
$0.00

Join my mailing list

* indicates required
Interests



Translate this page

Updated on 2026 Oct 2.

DISCLAIMER: This is not professional advice. The ideas and opinions presented here are my own, not necessarily those of my employer.