Raw code | Youtube demonstration
Note: this is NOT SECURE ENCRYPTION. It is fun to watch and play with, but it is NOT SECURE.

#!/usr/bin/env php
<?php 
$doSubstitution 
true;

function 
resetSpeed() {
    global 
$speed$speedslowfactor$speedmin;
    
$speed 400;
    
$speedslowfactor 1.1;
    
$speedmin 10;
}
resetSpeed();

$encrypt true;
echo 
"Encrypt (enter E) or decrypt (enter D)? ";
$encryptInput stream_get_line(STDIN1024PHP_EOL);
if (
$encryptInput == 'd' || $encryptInput == 'D') {
    
$encrypt false;
}

echo 
"Input password, end with enter. Press Ctrl+D to abort.\n";
$password stream_get_line(STDIN1024PHP_EOL);
if (!
$password) {
    die(
"Ctrl+D - exiting\n");
}

echo 
"Input message, end with enter. Press Ctrl+D to abort.\n";
$message stream_get_line(STDIN1024PHP_EOL);
if (!
$message) {
    die(
"Ctrl+D - exiting\n");
}

$closestSquare ceil(sqrt(strlen($message)));
$padding '';
for (
$i 0$i pow($closestSquare2) - strlen($message) - 1$i++) {
    
$padding .= chr(rand(32126));
}
if (
strlen($padding) > 0) {
    
$padding ' ' $padding;
}
$message .= $padding;

if (
$encrypt) {
    echo 
"Starting encryption. Our square is:\n";
    
displaySquare($message$closestSquare);
    echo 
"Expanding password... ";
    
$password keyExpansion($password);
    echo 
"New password: $password\n";

    if (
$doSubstitution) {
        echo 
"Applying password to each character...\n";
        for (
$i 0$i strlen($message); $i++) {
            
$passindex $i strlen($password);
            
$passchar $password[$passindex];
            
$newchar chr((((ord($message[$i]) - 32) + (ord($passchar) - 32) + $i) % 95) + 32);
            echo 
"Char $i (" $message[$i] . ', #' . (ord($message[$i]) - 32) . ") plus password char $passindex ($passchar, #" . (ord($passchar) - 32) . ") plus position $i results in $newchar (#" . (ord($newchar) - 32) . ")\n";
            
$message[$i] = $newchar;
            
displaySquare($message$closestSquare);
        }
        
resetSpeed();
        echo 
"Done applying character substitution. Now let's shift our square around!\n";
    }
    else {
        echo 
"Skipping substitution as requested...\n";
    }

    
$sq strToSq($message$closestSquare);
    for (
$i 0$i strlen($password) - 1$i += 2) {
        
$passchars $password[$i] . $password[$i 1];
        
$passvalue = (ord($password[$i]) - 32) * 96 ord($password[$i 1]) - 32;
        
$dir        $passvalue bindec('000000000001');
        
$horizshift $passvalue bindec('000000000010') >> 1;
        
$row        $passvalue bindec('000001111100') >> 2;
        
$amount     $passvalue bindec('111110000000') >> 7;
        
$amount    += $i;
        
$row       %= $closestSquare;
        
$dir        $dir : -1;
        
$horizshift $horizshift 0;
        
$amount    %= $closestSquare;
        
$amount    *= $dir;
        
$rowcol     $horizshift 'row' 'column';
        echo 
"Password chars $i and " . ($i 1) .  " ($passchars) make #" . ($passvalue 32) . " which give us $rowcol $row.\nWe will shift $amount times.\n";
        if (
$horizshift) {
            
$original $sq[$row];
            for (
$j 0$j $closestSquare$j++) {
                
$index = ($j $amount) % $closestSquare;
                if (
$index 0) {
                    
$index += $closestSquare;
                }
                
$sq[$row][$j] = $original[$index];
            }
        }
        else {
            
$original = [];
            for (
$j 0$j $closestSquare$j++) {
                
$original[] = $sq[$j][$row];
            }

            for (
$j 0$j $closestSquare$j++) {
                
$index = ($j $amount) % $closestSquare;
                if (
$index 0) {
                    
$index += $closestSquare;
                }
                
$sq[$j][$row] = $original[$index];
            }
        }
        
displaySquare2($sq);
    }

    echo 
"All done!\n";
    echo 
"Result: ";
    for (
$x 0$x $closestSquare$x++) {
        for (
$y 0$y $closestSquare$y++) {
            echo 
$sq[$y][$x];
        }
    }
    echo 
"\n";
}
else {
    echo 
"Starting decryption. Our square is:\n";
    
$sq strToSqRev($message$closestSquare);
    
displaySquare2($sq);

    
$originalPassword $password// We need this later for substitution
    
$password keyExpansion($password);
    echo 
"Expanding password... new password: $password\n";

    
$actionqueue = [];
    for (
$i 0$i strlen($password) - 1$i += 2) {
        
$passchars $password[$i] . $password[$i 1];
        
$passvalue = (ord($password[$i]) - 32) * 96 ord($password[$i 1]) - 32;
        
$dir        $passvalue bindec('000000000001');
        
$horizshift $passvalue bindec('000000000010') >> 1;
        
$row        $passvalue bindec('000001111100') >> 2;
        
$amount     $passvalue bindec('111110000000') >> 7;
        
$amount    += $i;
        
$row       %= $closestSquare;
        
$dir        $dir : -1;
        
$amount    %= $closestSquare;
        
$amount    *= $dir;
        
$actionqueue[] = [-$amount$horizshift$row$dir$passchars$passvalue];
    }

    
$actionqueue array_reverse($actionqueue);

    
$i 0;
    foreach (
$actionqueue as $action) {
        
$amount $action[0];
        
$horizshift $action[1];
        
$row $action[2];
        
$dir $action[3];
        
$passchars $action[4];
        
$passvalue $action[5];
        
$horizshift $horizshift true false;
        
$rowcol $horizshift 'row' 'column';
        echo 
"Password chars $i and " . ($i 1) .  " ($passchars) make #" . ($passvalue 32) . " which give us $rowcol $row.\nWe will shift $amount times.\n";
        
$i++;
        if (
$dir 0) {
            
$original $sq[$row];
            for (
$j 0$j $closestSquare$j++) {
                
$index = ($j $amount) % $closestSquare;
                if (
$index 0) {
                    
$index += $closestSquare;
                }
                
$sq[$row][$j] = $original[$index];
            }
        }
        else {
            
$original = [];
            for (
$j 0$j $closestSquare$j++) {
                
$original[] = $sq[$j][$row];
            }

            for (
$j 0$j $closestSquare$j++) {
                
$index = ($j $amount) % $closestSquare;
                if (
$index 0) {
                    
$index += $closestSquare;
                }
                
$sq[$j][$row] = $original[$index];
            }
        }
        
displaySquare2($sq);
    }

    
$message '';
    foreach (
$sq as $y) {
        
$message .= implode(''$y);
    }

    if (
$doSubstitution) {
        
resetSpeed();
        echo 
"Done shifting things around. Now reversing character substitution...\n";

        
// Apply substitution
        
for ($i 0$i strlen($message); $i++) {
            
$passindex $i strlen($password);
            
$passchar $password[$passindex];
            
$newchar chr(mod(((ord($message[$i]) - 32) - (ord($passchar) - 32) - $i), 95) + 32);
            echo 
"Char $i (" $message[$i] . ', #' . (ord($message[$i]) - 32) . ") minus password char $passindex ($passchar, #" . (ord($passchar) - 32) . ") minus position $i results in $newchar (#" . (ord($newchar) - 32) . ")\n";
            
$message[$i] = $newchar;
            
displaySquare($message$closestSquare);
        }
    }
    else {
        echo 
"Skipping substitution as requested\n";
    }

    
// Done!
    
echo "Done! Result: $message\n";
    echo 
"(Note that the last few characters are probably garbage.)\n";
}

function 
mod($n$m) {
    
$n $n $m;
    if (
$n 0)
        return 
$n;

    return (
$n abs($m)) % $m;
}

// Same as strToSq() except this reverts the x and y axis
function strToSqRev($str$size) {
    
$sq = [];
    for (
$x 0$x $size$x++) {
        
$tmpstr substr($str$x $size$size);
        for (
$y 0$y strlen($tmpstr); $y++) {
            if (!isset(
$sq[$y])) {
                
$sq[$y] = [];
            }
            
$sq[$y][$x] = $tmpstr[$y];
        }
    }
    return 
$sq;
}

function 
strToSq($str$size) {
    
$sq = [];
    for (
$y 0$y $size$y++) {
        
$tmpstr substr($str$y $size$size);
        
$sq[$y] = [];
        for (
$x 0$x strlen($tmpstr); $x++) {
            
$sq[$y][$x] = $tmpstr[$x];
        }
    }
    return 
$sq;
}

function 
displaySquare($message$size) {
    global 
$speed$speedslowfactor$speedmin;
    
$outstr '';
    for (
$y 0$y $size$y++) {
        
$str substr($message$y $size$size);
        for (
$i 0$i strlen($str); $i++) {
            
$outstr .= $str[$i] . ' ';
        }
        
$outstr .= "\n";
    }
    echo 
$outstr "\n";
    
usleep($speed 1000);
    
$speed max($speedmin$speed $speedslowfactor);
}

function 
displaySquare2($sq) {
    global 
$speed$speedslowfactor$speedmin;
    
$outstr '';
    foreach (
$sq as $y) {
        foreach (
$y as $x) {
            
$outstr .= $x ' ';
        }
        
$outstr .= "\n";
    }
    echo 
$outstr "\n";
    
usleep($speed 1000);
    
$speed max($speedmin$speed $speedslowfactor);
}

function 
keyExpansion($password) {
    
$password .= $password;
    
$originalPassword $password;
    
$i ord($password[0]) + 1;
    while (
strlen($originalPassword) > 1) {
        
$password .= str_repeat($originalPassword chr(($i 95) + 32), strlen($originalPassword));
        
$originalPassword substr($originalPassword1);
        
$i++;
    }
    
$a 43690;
    for (
$i 0$i strlen($password) - 1$i++) {
        
$password[$i] = chr((((ord($password[$i]) ^ $a) + $i) % 95) + 32);
        if (
$a == 43690) {
            
$a 21845;
        }
        else {
            
$a 43690;
        }
    }
    return 
$password;
}