JSFiddle - React, Tailwind, and code Playground

HTML

<div id="setup">
    <div>
        <span class="label">Rows:</span>
        <input id="rows" type="text" value="4" />
    </div>
    <div>
        <span class="label">Columns:</span>
        <input id="cols" type="text" value="8" />
    </div>
    <div>
        I recommend against any numbers above 20x20 or so. Reveal performance is poor.
    </div>
    <div>
        <button id="updateBtn">Draw!</button>
    </div>
</div>

<table id="spiral" cellspacing="0" cellpadding="0" border="0"></table>

CSS

body {
    font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;    
}

#updateBtn {
    margin: 0 100px;
    width: 100px;    
}

#setup>div {
    width: 300px;
    margin: 0 auto;  
    padding: 5px;    
    border-left: 1px solid blue;
    border-right: 1px solid blue;       
}

.label { 
    display: inline-block;
    width: 196px;
    margin: 0;
    padding: 0;
}

input {
    width: 96px; 
    margin: 0;
    padding: 0;    
    text-align: right;
}

#spiral {
    width: 100%;   
}

#spiral td {
    text-align: center;
    margin: 0;
    border: 1px solid black;
    box-shadow: inset 0 0 3px rgba(0,0,0,0.5);
}

JavaScript

var Spiral = (function() {   
    var color = function (value, total) {
        var v = Math.floor((1 - (value / total)) * 96) + 128;
        return ("rgb(" + v + ", " + v + ", " + v + ")");       
    };

    var draw = function ($table, rows, cols) {
        var r, c, $tr, $tAry = [], curNum = 0, p = { row: 0, col: 0 }, total = (rows * cols);

        var next = (function($tAry) { //call "next" with a position to get the next position.
            var d = {row: 0, col: 1}, dIndex = {row: 0, col: 0};

            return (function(p) {
                var isBarrier = function (r, c) { return (!$tAry[r] || !$tAry[r][c] || $tAry[r][c].text() !== "-"); };
                
                if (isBarrier(p.row + d.row, p.col + d.col)) {
                    d = {
                        row: (([0, 1, 0, -1])[(++dIndex.row) % 4]),
                        col: (([1, 0, -1, 0])[(++dIndex.col) % 4])
                    }; //will hit a barrier, turn!
                }
                
                return (isBarrier(p.row + d.row, p.col + d.col)) ? 
                    undefined : ({ row: (p.row + d.row), col: (p.col + d.col) });
            });
        }($tAry));

        $table.children().remove(); //clear the table.
        
        for(r = 0; r < rows; r++) {
            $tr = $("<tr />").appendTo($table); //Add the TR to the table.
            $tAry.push([]); //Add a row to the table array
            for(c = 0; c < cols; c++) {
                $tAry[$tAry.length-1].push($("<td>-</td>").css({
                    height: ($table.width() / cols), width: ($table.width() / cols)
                }).appendTo($tr)); //create a TD, add it to the table array and it's row.              
            }
        }
        
        (function f() { //All the cool kids live the slow reveal.
            $tAry[p.row][p.col].hide().text(curNum).css("background-color", color(curNum++, total)).fadeIn(); 
            if((p = next(p)) !== undefined) { //If we're not done, continue!
         ...