Spreading Christmas Cheer with PowerShell

Spreading Christmas Cheer with PowerShell

Most people use PowerShell for automation, reporting, or fixing things that are broken. This year, I decided to use it for something slightly different โ€” spreading a bit of Christmas cheer across the office.

What started as a simple โ€œletโ€™s make some snow fall on the screenโ€ quickly turned into a full festive animation running across every monitor. Snow, stars, baubles, falling gifts, Christmas trees, a big Santa face in the middle of the screen, and even a live countdown before it closes on its own. All written in PowerShell.

Why I Built It

IT can be non-stop in December. Patching, deployments, year-end changes, audits and last-minute requests all stack up. I thought it would be nice to break the routine for a moment and give the team something unexpected to smile at.

Instead of another wallpaper change or email, I wanted something that felt alive on screen โ€” something interactive, loud and unapologetically Christmas.

PowerShell turned out to be the perfect tool for exactly that.

What the Script Does

This script launches a transparent, full-screen festive overlay across every connected monitor. It includes:

  • Falling snow, stars, gifts and baubles

  • Rows of animated Christmas trees

  • A large Santa face in the centre of each screen

  • โ€œMerry Christmasโ€ displayed in gold across the middle

  • A live countdown timer

  • Automatic clean exit after 30 seconds

Everything runs directly from PowerShell using Windows Forms and GDI+ drawing โ€” no external files, no images, no media downloads.

Why PowerShell Is Perfect for This

PowerShell isnโ€™t just for admin tasks. Because it sits on top of the full .NET framework, you can create:

  • Graphical interfaces

  • Animated overlays

  • Real-time visual effects

  • Full screen applications

And you can do it all in a single script file that runs instantly on any Windows PC.

This project is a great reminder that PowerShell doesnโ€™t always have to be serious. Sometimes itโ€™s allowed to be fun.

Sharing a Bit of Christmas Spirit

I ran this quietly on a few screens in the office and waited. The reactions ranged from confusion to laughter to โ€œHow on earth did you do that with PowerShell?โ€

That alone made it worth it.

IT teams rarely get time to stop and enjoy moments like that. If a 30-second Santa animation helps lighten the mood in a busy December, then it has done its job.

Final Thoughts

PowerShell is an incredibly powerful tool, but itโ€™s also a creative one if you let it be. If you normally only use it for automation and fixes, try pushing it a bit further. You might surprise yourself with what it can do.

And more importantly โ€” have some fun with it.

Merry Christmas from Digital Geekery.

The Code

<#
.SYNOPSIS
Displays a full-screen multi-monitor Christmas animation using PowerShell.

.DESCRIPTION
This script creates a festive animated overlay across all connected monitors.
It includes falling snow, stars, gifts, and baubles, animated Christmas trees,
a large central Santa face, Merry Christmas text, and a live countdown timer.
The animation automatically closes after 30 seconds.

.AUTHOR
DIGITALGEEKERY

.VERSION
1.0

.NOTES
- Designed for Windows 10 / Windows 11.
- Requires .NET Framework with Windows Forms support.
- Runs safely in user context.
- Ideal for festive fun, demos, or seasonal IT morale boosters.

.EXAMPLE
powershell.exe -ExecutionPolicy Bypass -File .\Christmas-PowerShell.ps1

#>

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

Add-Type -ReferencedAssemblies "System.Windows.Forms","System.Drawing" @"
using System;
using System.Windows.Forms;

public class DoubleBufferedForm : Form
{
public DoubleBufferedForm()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
this.UpdateStyles();
}
}
"@

$rand = New-Object System.Random
$script:secondsLeft = 30
$script:lastSecondTick = [Environment]::TickCount

$forms = @()

foreach ($screen in [System.Windows.Forms.Screen]::AllScreens) {

$form = New-Object DoubleBufferedForm
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None
$form.Bounds = $screen.Bounds
$form.TopMost = $true
$form.BackColor = [System.Drawing.Color]::Black
$form.TransparencyKey = $form.BackColor
$form.ShowInTaskbar = $false

$flakes = @()
$ground = @{}

for ($i = 0; $i -lt 600; $i++) {

$type = $rand.Next(1, 5)
$icon = "snow"
$color = [System.Drawing.Color]::White

switch ($type) {
2 { $icon = "star"; $color = [System.Drawing.Color]::Gold }
3 { $icon = "gift"; $color = [System.Drawing.Color]::Red }
4 { $icon = "ball"; $color = [System.Drawing.Color]::DeepPink }
}

$flakes += [PSCustomObject]@{
X = $rand.Next($screen.Bounds.Left, $screen.Bounds.Right)
Y = $rand.Next($screen.Bounds.Top - 300, $screen.Bounds.Bottom)
Size = $rand.Next(6, 14)
Speed = $rand.Next(1, 5)
Drift = $rand.NextDouble() * 2.5 - 1.25
Alpha = 230
Landed = $false
Type = $icon
Color = $color
}
}

for ($x = $screen.Bounds.Left; $x -lt $screen.Bounds.Right; $x++) {
$ground[$x] = $screen.Bounds.Bottom - 40
}

$form.Tag = [PSCustomObject]@{
Screen = $screen
Flakes = $flakes
Ground = $ground
}

$form.Add_Paint({
param($sender, $e)

$data = $sender.Tag
$screen = $data.Screen
$flakes = $data.Flakes

$e.Graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality

# ๐ŸŽ„ Trees
for ($tx = $screen.Bounds.Left; $tx -lt $screen.Bounds.Right; $tx += 180) {

$treeBase = $screen.Bounds.Bottom - 10

$treeBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::DarkGreen)
$trunkBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::SaddleBrown)

$e.Graphics.FillEllipse($treeBrush, $tx + 10, $treeBase - 80, 80, 70)
$e.Graphics.FillEllipse($treeBrush, $tx + 20, $treeBase - 120, 60, 60)
$e.Graphics.FillRectangle($trunkBrush, $tx + 42, $treeBase - 5, 16, 12)

$treeBrush.Dispose()
$trunkBrush.Dispose()
}

# ๐ŸŽ… BIG CENTRED SANTA FACE
$centreX = $screen.Bounds.Left + ($screen.Bounds.Width / 2) - 120
$centreY = $screen.Bounds.Top + ($screen.Bounds.Height / 2) - 120

$faceBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::Bisque)
$beardBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::White)
$hatBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::Red)

$e.Graphics.FillEllipse($faceBrush, $centreX + 40, $centreY + 40, 160, 160)
$e.Graphics.FillEllipse($beardBrush, $centreX + 20, $centreY + 100, 200, 140)
$e.Graphics.FillRectangle($hatBrush, $centreX + 40, $centreY + 10, 160, 40)

$faceBrush.Dispose()
$beardBrush.Dispose()
$hatBrush.Dispose()

# ๐ŸŽ„ MERRY CHRISTMAS TEXT (CENTRE)
$font = New-Object System.Drawing.Font("Segoe UI", 48, [System.Drawing.FontStyle]::Bold)
$textBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::Gold)

$msg = "MERRY CHRISTMAS"
$textSize = $e.Graphics.MeasureString($msg, $font)

$textX = $screen.Bounds.Left + ($screen.Bounds.Width / 2) - ($textSize.Width / 2)
$textY = $centreY - 80

$e.Graphics.DrawString($msg, $font, $textBrush, $textX, $textY)

$font.Dispose()
$textBrush.Dispose()

# โ„ Falling items
foreach ($flake in $flakes) {

$brush = New-Object System.Drawing.SolidBrush (
[System.Drawing.Color]::FromArgb($flake.Alpha, $flake.Color)
)

if ($flake.Type -eq "gift") {
$e.Graphics.FillRectangle($brush, $flake.X, $flake.Y, $flake.Size, $flake.Size)
}
else {
$e.Graphics.FillEllipse($brush, $flake.X, $flake.Y, $flake.Size, $flake.Size)
}

$brush.Dispose()
}

# โฑ Countdown
$countFont = New-Object System.Drawing.Font("Segoe UI", 22, [System.Drawing.FontStyle]::Bold)
$countBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::White)

$countText = "Closing in $script:secondsLeft seconds"
$e.Graphics.DrawString($countText, $countFont, $countBrush, $screen.Bounds.Left + 20, $screen.Bounds.Top + 20)

$countFont.Dispose()
$countBrush.Dispose()
})

$forms += $form
}

# Animation timer
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 30
$timer.Add_Tick({

if ([Environment]::TickCount - $script:lastSecondTick -ge 1000) {
if ($script:secondsLeft -gt 0) { $script:secondsLeft-- }
$script:lastSecondTick = [Environment]::TickCount
}

foreach ($form in $forms) {

$data = $form.Tag
$screen = $data.Screen
$flakes = $data.Flakes
$ground = $data.Ground

foreach ($flake in $flakes) {

if (-not $flake.Landed) {

$flake.Y += $flake.Speed
$flake.X += $flake.Drift

if ($flake.X -lt $screen.Bounds.Left) { $flake.X = $screen.Bounds.Right }
if ($flake.X -gt $screen.Bounds.Right) { $flake.X = $screen.Bounds.Left }

$xInt = [int][Math]::Round($flake.X)

if ($flake.Y + $flake.Size -ge $ground[$xInt]) {
$flake.Landed = $true
}
}
else {
if ($rand.NextDouble() -lt 0.02) {
$flake.Y = $screen.Bounds.Top
$flake.X = $rand.Next($screen.Bounds.Left, $screen.Bounds.Right)
$flake.Landed = $false
}
}
}

$form.Invalidate()
}
})

$timer.Start()

# Auto close
$closeTimer = New-Object System.Windows.Forms.Timer
$closeTimer.Interval = 30000
$closeTimer.Add_Tick({
$timer.Stop()
foreach ($form in $forms) { $form.Close() }
})
$closeTimer.Start()

# โœ… Show ALL forms correctly
foreach ($form in $forms) {
$form.Show()
}

while ($forms | Where-Object { $_.Visible }) {
[System.Windows.Forms.Application]::DoEvents()
Start-Sleep -Milliseconds 50
}

Total
0
Shares
Previous Post

Rufus: The Ultimate Tool for Creating Bootable USB Drives

Next Post

Bulk Add Windows Autopilot Devices to Entra ID Groups Using Only Serial Numbers

Related Posts