JSFiddle - React, Tailwind, and code Playground

HTML

<h1>Thue interpreter in Javascript</h1>
        <i>It is pitch black. You are likely to be eaten by a Thue.</i><br>
        Sample programs: 
        <a onclick="load('hello'); return false;" href="#">hello world</a>,
        <a onclick="load('sierpinski'); return false;" href="#">Sierpinski's triangle</a><br>
        <textarea rows="15" cols="70" id="code"></textarea><br>
        <div id="workspace">&nbsp;</div><br>
        <button onclick="init()">Update</button>
        <button onclick="do_run()">Run</button>
        <button onclick="do_step()">Step</button>
        <br><br>
        <div id="output"></div><br>

CSS

td {vertical-align:top} th {text-align:left} #output div {margin-left:1em;font-family:monospace} #output {color:black;background:white;font-family:monospace;padding:0em 0.2em}
td {vertical-align:top} th {text-align:left} #workspace div {margin-left:1em;font-family:monospace} #workspace {color:black;background:#ffffee;font-family:monospace;padding:0em 0.2em}

JavaScript

// These global variables keep the state of the code:
// Execution doesn't influence them, only the user

rules = {}
dataspace = ""

// These global variables keep the state of the execution:

workspace = ""
state = 'nothing'
magic = ''
matchindex = 0
matchlen = 0
selected_rule = ''
output_text = ''

// Code follows.

function init_execution()
{
    workspace = dataspace
    state = 'nothing'
    magic = ''
    matchindex = 0
    matchlen = 0
    selected_rule = ''
}

function step()
{
    // Steps once through the execution. Modifies
    // the globals which keep the state of the execution.
    // Doesn't display anything.
    output_text = ''
    magic = ''
    if (state == 'done') return;
    if (state == 'nothing' || state == 'changed') {
        matching_rules = []
        for (rule in rules) {
            matches = all_matches(rule, workspace)
            if (matches.length > 0) {
                matching_rules.push([rule, matches])
            }
        }
        if (matching_rules.length == 0) {
            state = 'done'
            return
        }
        var selected = random_choice(matching_rules)
        selected_rule = selected[0]
        matchindex = random_choice(selected[1])
        matchlen = selected_rule.length
        state = 'selected'
        return
    } else if (state == 'selected') {
        state = 'changed'
        var selected_rhs = random_choice(rules[selected_rule])
        if (selected_rhs == '') {
            magic = 'empty'
            workspace = workspace.substring(0,matchindex)+
                        workspace.substring(matchindex+matchlen,workspace.length)
            matchlen = 0
            return
        }
        if (selected_rhs.charAt(0) == '~') {
            // handle output!
            magic = 'output'
            output_text = selected_rhs.substring(1,selected_rhs.length)
            workspace = workspace.substring(0,matchindex)+
                        workspace.substring(matchindex+matchlen,workspace.length)
  ...