Skip to content

Instantly share code, notes, and snippets.

@daisyUniverse
Created March 21, 2025 20:38
Show Gist options
  • Save daisyUniverse/f60f921e28af58b95071054b1a64091d to your computer and use it in GitHub Desktop.
Save daisyUniverse/f60f921e28af58b95071054b1a64091d to your computer and use it in GitHub Desktop.
# Screenbuffer Experiments
# More experiments in learning how to implement a fast powershell screenbuffer
# Robin Universe [D]
# 03 . 05 . 25
param(
[switch]$stepthrough,
[switch]$cls,
[switch]$slow
)
# X IS SIDE TO SIDE ($pos[0])
# Y IS UP AND DOWN ($pos[1])
[Console]::SetCursorPosition(0,0)
[Console]::CursorVisible = $false
$Esc=[char]0x1b
if ($cls){Clear-Host}
function rgb( [int[]]$rgb=($null), [int[]]$bgrgb=($null), [string]$string, [switch]$NoReset){
if (!$NoReset) { $rst = "$Esc[0m" } else { $rst="" } # Reset control code
$bgc = "$Esc[48;2;" # Background control code
$fgc = "$Esc[38;2;" # Foreground control code
if ($null -ne $rgb ) { $out = ( $fgc + $rgb[0] + ";" + $rgb[1] + ";" + $rgb[2] + "m" ) } # if there is a fg rgb value, apply
if ($null -ne $bgrgb) { $out += ( $bgc + $bgrgb[0] + ";" + $bgrgb[1] + ";" + $bgrgb[2] + "m" ) } # if there is a bg rgb value, apply
Return ($out + $string + $rst) # Return string with ansi control characters
}
class hue {
[int]$speed
[int[]]$rgb
[bool[]]$up = ( $true, $true, $true )
$scd = $this.speed
hue() {
$this.speed = 5 # Speed controller, higher = slower
$this.scd = $this.speed
$this.rgb = ( 0, 0, 0 ) # Initial RGB values
$this.up = ( $true, $true, $true ) # Indicator for if the values are rising or falling
}
hue([int]$speed, [int[]]$rgb){
$this.speed = $speed
$this.rgb = $rgb
$this.scd = $speed
}
[string] increment(){
$i=0; $this.scd-- # Init counters
if ($this.scd -lt 0){ # Repeat same values for as long as SCD (Speed Countdown) is above zero
foreach ($color in $this.rgb){
$sup = $this.up[$i] # Grab direction
$scale = [Math]::Abs( $color - 128 ) # Find current distance to 128
$normalize = ( $scale - -255 ) / ( 255 - -255 ) # Normalize scale for easing
if ( $sup ) { $color += [Math]::Floor( $normalize + 1) } # Add normalized scale going upwards
elseif ( $false -eq $sup ) { $color -= [Math]::Floor( $normalize + 1) } # Sub normalized scale going downwards
if ( $color -gt 253 ) { $sup = $false; $color = 253 } # Clamp value max
elseif ( $color -lt 55 ) { $sup = $true; $color = 55 } # Clamp value min
$this.scd = $this.speed # Apply loop changes
$this.rgb[$i] = $color
$this.up[$i] = $sup
$i++
}
}
Return $this.rgb
}
}
function Get-Canvas-Size {
$window = [PSCustomObject]@{
height = (Get-Host).UI.RawUI.MaxWindowSize.Height
width = (Get-Host).UI.RawUI.MaxWindowSize.Width
}
return $window
}
function BuildBuffer($window, $char="") {
$l = 0
$buffer = [string[]]::new($window.height)
while ($l -lt ($window.height -1)){
$buffer[$l] = ($char * $window.width); $l++
}
return $buffer
}
function BuildColorBuffer($window, $buffer){
}
function Render($buffer){
[Console]::SetCursorPosition(0,0)
foreach ($line in $buffer){ if ( $line -gt $window.width ) { $line = $line.Substring(0, $window.width) } }
[Console]::Write($buffer -join "$Esc[0m`n")
}
class canvas{
$window
canvas() {
$this.window = Get-Canvas-Size
}
# Issue: The positioning of the string is skewed by the length of the string being different with ansi control characters
# IE: "X" becomes "[38;2;255;255;255m[48;2;21;23;255mX[0m" when given an RGB ansi control code
# This causes the screenbuffer to have unwritable space, or go out of bounds when attempting to modify the buffer line
[string] SetCharacter([int[]]$pos, [string]$char, [string]$buffer, [int[]]$rgb=$null, [int[]]$bgrgb=$null) {
$lines = $buffer.split("`n") # Splits original buffer into an array of lines
$l = $char.Length
$char = (rgb -rgb $rgb -bgrgb $bgrgb -string $char) # Prepends RGB ansi code to string
$charleng = $char.split([char]0x1b).Length
Write-Host $charleng
$lines[$pos[1]] = $lines[$pos[1]].Remove($pos[0], $l).Insert($pos[0],$char) # Replaces characters starting at specified coordinate
$buffer = $lines -join "`n" # Stitches the buffer back together
Return $buffer
}
[string] Get([int]$x, [int]$y) {
Return " "
}
}
function Get-NormalizeCoordinates([int[]]$pos){
$window = Get-Canvas-Size
$normalizedX = ($pos[0] / $window.width * 2) -1
$normalizedY = ($pos[1] / $window.height * 2) -1
return ($normalizedX, $normalizedY)
}
function Set-NormalizedCoordinates([double[]]$pos){
$window = Get-Canvas-Size
$screenX = [Math]::Round( ( ( $pos[0] + 1 ) / 2) * $window.width )
$screenY = [Math]::Round( ( ( $pos[1] + 1 ) / 2) * $window.height )
return ($screenX, $screenY)
}
$window = Get-Canvas-Size
$buffer = BuildBuffer $window "X"
$canvas = [canvas]::new()
function rect($char = " ", [int[]]$size = (1,1) , [int[]]$pos = (0,0), $buffer, [int[]]$rgb, [int[]]$bgrgb){
$x=0; $y=0
$char = $char * $size[0]
while ($x -lt $size[1]){
$buffer = $canvas.SetCharacter( ( $pos[1], ($pos[0] + $x) ), $char, $buffer, $rgb, $bgrgb )
$x++
}
return $buffer
}
if($stepthrough){ # Advance frame by frame
$pos = 0, 0
while($true){
if ([console]::KeyAvailable){
$x = [System.Console]::ReadKey($true)
if ($x.KeyChar -eq 'w'){
$pos[1] = $pos[1] - 1
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer)
Render $buffer
}
if ($x.KeyChar -eq 's'){
$pos[1] = $pos[1] + 1
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer)
Render $buffer
}
if ($x.KeyChar -eq 'a'){
$pos[0] = $pos[0] - 1
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer)
Render $buffer
}
if ($x.KeyChar -eq 'd'){
$pos[0] = $pos[0] + 1
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer)
Render $buffer
}
}
}
}else{ # Render as fast as possible.
Measure-Command {
while($true){
$CS = Get-Canvas-Size
if (($window.height -ne $CS.height) -or ($window.width -ne $CS.width)){$window = $CS; $buffer = BuildBuffer $window "X"} # Change buffer size as window size changes
$normalizedCoords = @(0.5, -0.5)
$fg = (255, 255, 255)
$bg = (21, 23, 255)
$screenCoords = Set-NormalizedCoordinates $normalizedCoords
#$buffer = (Insert-BufferString $buffer $screenCoords "FUCK").ToString()
#$buffer = $canvas.SetCharacter( $screenCoords, "X", $buffer, $fg, $bg)
if ($slow){sleep 1}
Render $buffer
#Write-Host ("The window is " + $window.height + " tall, and is " + $window.width + " wide ")
#for ($y = 0; $y -lt $window.height; $y++) {
# for ($x = 0; $x -lt $window.width; $x++) {
# }
#}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment