Versions

Added in 2026Version 3.0 (November 2026) [source code]

Version 2.1 (October 2015)

Version 2.0 (October 2015)

Version 1.0 (October 2015)

Live demo

Contents
  1. What is it?
  2. How does it work? (nontechnically)
  3. What was added in 2026?
  4. You keep insisting on “randomly,” but is it truly random?
  5. Does it really mimic evolution?
  6. Does it simulate intelligence?
  7. Is such an experiment a new thing?
  8. Are the monkeys able to type any text?
  9. How does it work? (technically)
  10. Is it useful?
  11. Do you simulate primate bones, hand muscles…?
  12. (Misc.)
  13. Can I ask more questions, or correct you?

What is it?

This is a genetic algorithm related to the infinite monkey theorem.

My algorithm simulates generations and generations of virtual monkeys randomly typing on a keyboard to produce the Shakespearean tragedy Hamlet.

GeneDNAFingerCondition[shift]Character
11000010000018: right ring.32pressedL
20110011010016: right index.52pressedU
30010000000012: left middle0pressedC
40111011111117: right middle.63pressedI
…
90100010001104: left thumb.35not pressed[space]
…
130110001010006: right index.2not pressedn
140010100010002: left middle.68not pressede
151001100010009: right pinky.68not pressedp
Eight of the 70 genes of the monkey that typed line 17 of Hamlet, LUCIANUS nephew to the king., as the server recorded them in March 2016. The screenshot shown here in 2015 is lost: this table was rebuilt in 2026 from the same data.

How does it work? (nontechnically)

  1. Twenty virtual monkeys are created.
  2. Each of them has a randomly generated binary DNA (10110…).
  3. Each of their genes is decoded into a group of three pieces of data (see the example above; on the live page, this data is available in the details of each line typed in your browser):
    • a finger;
    • the condition of this finger (folded, straight…);
    • whether the monkey must press the [shift] key.
  4. This data is applied to a virtual standard US keyboard.
  5. A character is created (a, Z, 5, @, [tab]…) according to the finger to use and its position over the keyboard (see this example), and to whether the [shift] key is pressed or not.
  6. The order of the genes in the DNA gives the sequence of fingers to activate: a sentence is produced.
  7. The program, which “knows” Hamlet, compares the sentences generated by the monkeys with the original one and rates the monkeys between 0 and 1. The closer the score is to 1, the closer the generated sentence is to the expected one. The monkeys never have access to the original sentence: the only information they get is this score. If the sentence is found at this stage (in the first generation, that is: never), the loop restarts with new monkeys [goto b].
  8. When all the sentences have been produced and rated, the monkeys are ranked. The monkey closest to the original sentence is cloned for the next generation, and is guaranteed the opportunity to reproduce. The other monkeys have a chance of reproducing that is proportional to their score.
  9. The selected monkeys have sex with randomly selected monkeys of the group. They have babies. Each baby’s DNA is a mix of the DNA of both parents. [Update v. 2.1: each couple has two babies.]
  10. The genes of the babies’ DNA are randomly mutated (1% of the genes). The babies become adults. The process continues [goto c] until the sentence is found.
  11. Once the sentence is found, the loop restarts with new monkeys (entirely new DNA) [goto b].
  12. Once the whole book has been produced, the loop restarts from the beginning to produce another one [goto a].

Added in 2026What was added in 2026?

The program of 2015 ran on a server, day and night, and this site only showed what it wrote. That server is gone. Here is everything 2026 added to bring the monkeys back, and what it left alone.

  • The program runs in your browser. The C++ code of 2015 is compiled to WebAssembly, a format that browsers run. Your browser gives the program what the server gave it: a folder in which to write its text files. The live page reads those files, as the 2015 page did.

  • Six lines of code. Four of the five files of 2015 are compiled exactly as written. Source.cpp gains six lines and loses none: two calls to the browser, their two declarations, a comment and a blank line. The first call asks “which line do I type?” The second, before each generation, hands control back to the browser: it shows the best attempt, and waits when the monkeys go too fast. The source code page shows them in green.

  • The pace. A recent laptop runs the program about ten times faster than the server did. The page holds it back to 760 generations per second, the speed measured on the server in March 2016 (630,209 generations in 829 seconds). Each generation is shown as it is computed: nothing is prepared in advance, and nothing knows when a line will be found.

  • The relay. One Hamlet takes about 26 hours at that pace, and nobody watches that long. So the visitors take turns. A small program in front of this site (a Cloudflare Worker) keeps the troop’s place: the Hamlet it is on, the line it has reached, and how many lines have been typed since the count began. When you open the live page, your browser asks for the line and types it. When your monkeys find it, your browser says so, the count grows by one, and the next line goes to whoever is watching at that moment: you, if you stay. If two visitors watch at the same time, both type the same line, each with their own monkeys, and the first to finish is counted.

  • When the relay does not answer. The monkeys type alone: your browser goes on with the next line by itself, the page says so, and nothing is counted. It asks again every minute.

What 2026 did not touch: the genetic algorithm, the keyboard and the fingers, the score, and the randomness. As in 2015, the random generator is seeded once, when the program starts. So the monkeys of your browser are yours alone: another visitor’s monkeys type the same line in their own way.

You keep insisting on “randomly,” but is it truly random?

Yes and no.

Does it really mimic evolution?

Yes and no.

Does it simulate intelligence?

Not at all.

The monkeys are not able, in general, to generate grammatically valid or seemingly meaningful sentences. If they do, it is only by accident or as a result of the evolutionary process. Suppose I had hidden how this website works, and said: “Look! I have created virtual monkeys that are able to understand any book and, after some hesitation, to rewrite it without copying it. They internalize the meaning of each sentence. I will not show you the code, but I will ask an indisputable C++ expert to confirm it.” You might reply: “No way! Monkey see, monkey do!” But because this expert, after taking a look at the source code, would say: “Indeed, there are creatures with no copy/paste function, and they produce the sentences by themselves,” you would probably think that such extraordinary creatures understand the English language (well, I know you are not that naive). If I put my program into a black box, I produce an experiment close to the Chinese Room argument, in order to give the (false) impression that my monkeys are smart.

But they are not, I insist. They have no brain; their only purpose is to type a sequence of keys in the hope of having sex with a random peer—like on a dating app.

In the first version, the title read: “. . . Monkeys Writing . . .” I changed it to “. . . Monkeys Typing . . .” because it seems to me that the act of writing has a different meaning from that of typing. Writing implies comprehending the result of the input, while typing focuses more on the input process. Cf. the hyperbolic “That’s not writing, that’s typing” (Capote on Kerouac).

Is such an experiment a new thing?

No (and yes).

Are the monkeys able to type any text?

Yes, provided that its lines (lines, not sentences) are no more than 70 characters long and use only characters available on a standard US keyboard. It will work without any modification of the code.

How does it work? (technically)

This is a C++ program. The source code is available online.

Randomness

Engine: std::mt19937. std::uniform_int_distribution<int> randna randomly produces a 0 or a 1 (an int), which is converted to a char ('0', '1') with the formula (int) + '0';

Added in 2026[Update v. 3.0 (2026): the engine is seeded once, when the program starts, from std::random_device: in 2015 that was when the server started, now it is each time the live page is opened. The engine is the same, but the compiler and the C++ library are not, and the C++ standard leaves it to each library how a distribution turns the engine’s output into numbers: from the same seed, the 2015 program would have drawn other numbers.]

DNA

Per monkey: a string of 840 bits (70 genes × 12 bits), initialized to 0 and then randomized.

Genes: 12 bits long. The first 4 bits encode the finger (an int); the next 7 bits encode the condition of the finger (a double, from 0 to 1); the last bit encodes whether the [shift] key is pressed (a bool: 0 [false] or 1 [true]).

Example: 011000010001 is equivalent to 0110 0001000 1, where

0110    => (int)    6    : (6 + 1)th finger, right index
0001000 => (double) .08  : finger very folded
1       => (bool)   true : [shift] pressed

Decode function: makes a tuple(int, double, bool) from the substring of a gene. Decoder: std::stoi(genes, nullptr, 2);

Some genes are repressed because they represent an impossible state of the world. Example: a 12th finger.

Fingers

From 0, the left pinky, to 9, the right pinky, with the thumbs side by side, that is:

  1. left pinky
  2. left ring
  3. left middle
  4. left index
  5. left thumb
  6. right thumb
  7. right index
  8. right middle
  9. right ring
  10. right pinky

Condition of the finger

The closer to the bottom of the keyboard, the closer to 0 (folded finger); the closer to the top of the keyboard (below the function keys), the closer to 1 (straightened finger). This is not a function based on scientific facts: it is a convenient and intuitive way to express the position of a finger above a keyboard and the “effort” needed to reach a key.

0 [folded] -- .5 [half-straightened] -- 1 [fully straightened]

Keyboard

ANSI INCITS 154-1988 (US standard).

All the keys are available, except:

Thus, the monkeys use 96 different characters, including [Tab] and the grave accent.

The keyboard is hard-coded. Each finger has access to only a subset of the keyboard, like this.

The US keyboard of the program, each key coloured by the finger that can reach it The table below the drawing lists the keys of each finger, from the most folded position to the straightest. ` ~: left pinky (finger 0), rank 5 (its ranks go from 0 to 5), condition from .84 to 1; types `, or ~ with shift~`5 1 !: left pinky (finger 0), rank 4 (its ranks go from 0 to 5), condition from .67 to .83; types 1, or ! with shift!14 2 @: left ring (finger 1), rank 3 (its ranks go from 0 to 3), condition from .75 to .99; types 2, or @ with shift@23 3 #: left middle (finger 2), rank 3 (its ranks go from 0 to 3), condition from .75 to .99; types 3, or # with shift#33 4 $: left index (finger 3), rank 7 (its ranks go from 0 to 7), condition from .88 to .99; types 4, or $ with shift$47 5 %: left index (finger 3), rank 6 (its ranks go from 0 to 7), condition from .75 to .87; types 5, or % with shift%56 6 ^: right index (finger 6), rank 7 (its ranks go from 0 to 7), condition from .88 to .99; types 6, or ^ with shift^67 7 &: right index (finger 6), rank 6 (its ranks go from 0 to 7), condition from .75 to .87; types 7, or & with shift&76 8 *: right middle (finger 7), rank 3 (its ranks go from 0 to 3), condition from .75 to .99; types 8, or * with shift*83 9 (: right ring (finger 8), rank 3 (its ranks go from 0 to 3), condition from .75 to .99; types 9, or ( with shift(93 0 ): right pinky (finger 9), rank 9 (its ranks go from 0 to 9), condition from .9 to .99; types 0, or ) with shift)09 - _: right pinky (finger 9), rank 8 (its ranks go from 0 to 9), condition from .8 to .89; types -, or _ with shift_-8 = +: right pinky (finger 9), rank 7 (its ranks go from 0 to 9), condition from .71 to .79; types =, or + with shift+=7 Backspace: not available to the monkeysBackspace Tab: left pinky (finger 0), rank 3 (its ranks go from 0 to 5), condition from .51 to .66; types a tab, with or without shiftTab3 Q: left pinky (finger 0), rank 2 (its ranks go from 0 to 5), condition from .34 to .5; types q, or Q with shiftQ2 W: left ring (finger 1), rank 2 (its ranks go from 0 to 3), condition from .5 to .74; types w, or W with shiftW2 E: left middle (finger 2), rank 2 (its ranks go from 0 to 3), condition from .5 to .74; types e, or E with shiftE2 R: left index (finger 3), rank 5 (its ranks go from 0 to 7), condition from .63 to .74; types r, or R with shiftR5 T: left index (finger 3), rank 4 (its ranks go from 0 to 7), condition from .5 to .62; types t, or T with shiftT4 Y: right index (finger 6), rank 5 (its ranks go from 0 to 7), condition from .63 to .74; types y, or Y with shiftY5 U: right index (finger 6), rank 4 (its ranks go from 0 to 7), condition from .5 to .62; types u, or U with shiftU4 I: right middle (finger 7), rank 2 (its ranks go from 0 to 3), condition from .5 to .74; types i, or I with shiftI2 O: right ring (finger 8), rank 2 (its ranks go from 0 to 3), condition from .5 to .74; types o, or O with shiftO2 P: right pinky (finger 9), rank 6 (its ranks go from 0 to 9), condition from .61 to .7; types p, or P with shiftP6 [ {: right pinky (finger 9), rank 5 (its ranks go from 0 to 9), condition from .5 to .6; types [, or { with shift{[5 ] }: right pinky (finger 9), rank 4 (its ranks go from 0 to 9), condition from .4 to .49; types ], or } with shift}]4 \ |: right pinky (finger 9), rank 3 (its ranks go from 0 to 9), condition from .31 to .39; types \, or | with shift|\3 Caps Lock: not available to the monkeysCaps Lock A: left pinky (finger 0), rank 1 (its ranks go from 0 to 5), condition from .17 to .33; types a, or A with shiftA1 S: left ring (finger 1), rank 1 (its ranks go from 0 to 3), condition from .25 to .49; types s, or S with shiftS1 D: left middle (finger 2), rank 1 (its ranks go from 0 to 3), condition from .25 to .49; types d, or D with shiftD1 F: left index (finger 3), rank 3 (its ranks go from 0 to 7), condition from .38 to .49; types f, or F with shiftF3 G: left index (finger 3), rank 2 (its ranks go from 0 to 7), condition from .25 to .37; types g, or G with shiftG2 H: right index (finger 6), rank 3 (its ranks go from 0 to 7), condition from .38 to .49; types h, or H with shiftH3 J: right index (finger 6), rank 2 (its ranks go from 0 to 7), condition from .25 to .37; types j, or J with shiftJ2 K: right middle (finger 7), rank 1 (its ranks go from 0 to 3), condition from .25 to .49; types k, or K with shiftK1 L: right ring (finger 8), rank 1 (its ranks go from 0 to 3), condition from .25 to .49; types l, or L with shiftL1 ; :: right pinky (finger 9), rank 2 (its ranks go from 0 to 9), condition from .2 to .3; types ;, or : with shift:;2 ' ": right pinky (finger 9), rank 1 (its ranks go from 0 to 9), condition from .1 to .19; types ', or " with shift"'1 Enter: not available to the monkeysEnter Shift: not available to the monkeysShift Z: left pinky (finger 0), rank 0 (its ranks go from 0 to 5), condition from 0 to .16; types z, or Z with shiftZ0 X: left ring (finger 1), rank 0 (its ranks go from 0 to 3), condition from 0 to .24; types x, or X with shiftX0 C: left middle (finger 2), rank 0 (its ranks go from 0 to 3), condition from 0 to .24; types c, or C with shiftC0 V: left index (finger 3), rank 1 (its ranks go from 0 to 7), condition from .13 to .24; types v, or V with shiftV1 B: left index (finger 3), rank 0 (its ranks go from 0 to 7), condition from 0 to .12; types b, or B with shiftB0 N: right index (finger 6), rank 1 (its ranks go from 0 to 7), condition from .13 to .24; types n, or N with shiftN1 M: right index (finger 6), rank 0 (its ranks go from 0 to 7), condition from 0 to .12; types m, or M with shiftM0 , <: right middle (finger 7), rank 0 (its ranks go from 0 to 3), condition from 0 to .24; types ,, or < with shift<,0 . >: right ring (finger 8), rank 0 (its ranks go from 0 to 3), condition from 0 to .24; types ., or > with shift>.0 / ?: right pinky (finger 9), rank 0 (its ranks go from 0 to 9), condition from 0 to .09; types /, or ? with shift?/0 Shift: not available to the monkeysShift Ctrl: not available to the monkeysCtrl Win: not available to the monkeysWin Alt: not available to the monkeysAlt Space bar: left thumb (finger 4) or right thumb (finger 5), whatever the condition of the thumb, with or without shiftspace · thumbs 4 and 5 Alt: not available to the monkeysAlt Win: not available to the monkeysWin Menu: not available to the monkeysMenu Ctrl: not available to the monkeysCtrl 0 1 2 3 6 7 8 9
FingerKeys, from the most folded position (rank 0) to the straightestWidth of a position
0 left pinkyz a q Tab 1 `.167
1 left ringx s w 2.25
2 left middlec d e 3.25
3 left indexb v g f t r 5 4.125
4, 5 thumbsspace (whatever the condition)–
6 right indexm n j h u y 7 6.125
7 right middle, k i 8.25
8 right ring. l o 9.25
9 right pinky/ ' ; \ ] [ p = - 0.1

The keyboard as the program encodes it (Type.cpp), drawn in 2026 from the code itself: the image of 2015 showed the first lines of that code. Each key has the colour of the only finger that can reach it, and the fingers are numbered above the top row. The small number on a key is its rank among the positions of its finger, from 0 (the most folded). The keys with a dashed outline are not available to the monkeys. [shift] is not pressed by a finger: it is the last bit of each gene.

A finger presses its key of rank k when its condition is at least k times the width given in the table, and less than k + 1 times that width. Beyond its last key, the gene is repressed. With [shift], the same position gives the other character of the key (A for a, ! for 1, and so on).

If an impossible finger (> 9) or position (> 1) is given, it means that there is an anomaly in the gene: it is repressed, and the keyboard returns char(0), that is, null (because no action is performed on it).

Added in 2026[Update v. 3.0 (2026): this is not true of the thumbs. In the code, a thumb presses the space bar whatever its condition, even above 1: the seven bits of a condition, divided by 100, go up to 1.27.]

Evaluation

The sentence generated by each monkey, once its DNA has been decoded and the keys pressed, is compared, char by char, with the original sentence. The two sentences are kept strictly apart: they only come into contact in an rvalue, the computation of the absolute distance between them.

The monkeys’ “intelligence” (a convenient but probably biased way of saying “fitness”) is evaluated as a double:

1 divided by (absolute distance between the generated sentence and the reference sentence + 1)2

This lvalue does not contain any direct information about the content of the reference sentence. Only this double, between 0 and 1.

That’s it.

Selection

The monkeys are ranked according to their intelligence.

Reproduction

Two modes are combined: elite and roulette.

Elite: the best monkey is cloned twice, and its two clones are monkeys 0 and 1 of the next generation. Clone 0’s DNA remains intact in the next generation; clone 1’s DNA can be mutated before the next generation. This may look like a waste, but the best monkey quickly ends up being monkey 0 or monkey 1, so it clones into itself and one other monkey.

Roulette: statistically, the chance that a monkey becomes a breeder is proportional to its intelligence.

Reproduction (50/50 crossover): the baby monkeys (temporary monkeys 20 to 39) receive the first half of their DNA from a breeder, and the second half from a random monkey (std::uniform_int_distribution<int> randomonkey).

[Update v. 2.1: and vice versa. Each couple has two babies. The first baby gets its DNA from parent A and parent B, in that order; the second baby gets its DNA from parent B and parent A, in that order.]

Parent A: 10011 . . . 01010 (selected breeder)
Parent B: 00101 . . . 11101 (random monkey)
Baby A  : 10011 . . . 11101
Baby B  : 00101 . . . 01010 < update v. 2.1

Fun facts: the monkeys are neither male nor female (no identified sex), and a monkey can mate with itself. You may think that this is useless, until you realize that the resulting DNA can be mutated.

Mutation

No mutation = no genetic algorithm.

The mutation rate is set at 1%.

For each position of a character in a sentence (length: 70), a random number from 0 (included) to 100 (excluded) is generated (std::uniform_int_distribution<int> randomutation). If this number is strictly less than the mutation rate, the whole gene at this position is mutated at random: the finger can change, the condition of the finger can change, the state of [shift] can change.

Example:

0110    => (int)    6    : (6 + 1)th finger, right index
0001000 => (double) .08  : finger very folded
1       => (bool)   true : [shift] pressed

becomes (arbitrary example):

0100    => (int)    4    : (4 + 1)th finger, left thumb
1011101 => (double) .93  : finger very straightened
0       => (bool)   false: [shift] NOT pressed

Is it useful?

No. Or perhaps to illustrate the myth of Sisyphus.

Do you simulate primate bones, hand muscles…?

No. Do not expect a VR-ready 3D simulation of the monkeys either. There is nothing beyond the 2D, 44 × 11 px, non-animated fingers on the statistics pages. My monkeys are nothing more than hands and implicit, unidentified genitals.

(Misc.)

What are the numbers next to the sentences, on the statistics page?

These numbers are the “intelligence” (that is, the fitness) of the monkey that produced the sentence, from .000… to 1.

Sometimes, chunks of characters jump to the left or to the right. Why?

As displayed

(0.00000776)  MJCH<<Id   llklmd kg wxk ickc0
(0.00000935)  MJCH<<Id    llklmd kg wxk ickc0

Gene by gene

(0.00000776)  MJCH<<Id  [nothing] llklmd kg wxk ickc0
(0.00000935)  MJCH<<Id    llklmd kg wxk ickc0
Two successive best attempts at line 17 of Hamlet, LUCIANUS nephew to the king., with their scores, as the server recorded them in March 2016 (improvements 104 and 105 out of 184; the first 31 of the 70 characters). In the first attempt, gene 11 is repressed (the empty box): it types nothing, so everything after it is displayed one column to the left. In the second, the same gene presses the space bar (the filled box): the highlighted chunk, which will become nephew to the king., jumps one column to the right. The image shown here in 2015 is lost: this example was rebuilt in 2026.

Some genes are not expressed (because they represent an impossible finger or an impossible condition of the finger). When a gene is repressed, the keyboard returns “nothing” (char(0), null) because no action is performed on it. Sometimes, a mutation turns a repressed gene into an expressed one. When this new gene means that the hand presses [space] or [tab], a null char is replaced by this space: a jump to the right occurs. And vice versa, when a mutation represses a gene.

Can I ask more questions, or correct you?

Sure!

Feel free to contact me. I may add your (anonymized) question here, along with my answer.

Live demo