Skip to content

Instantly share code, notes, and snippets.

@R3V1Z3
Last active December 14, 2025 21:28
Show Gist options
  • Select an option

  • Save R3V1Z3/8371822334eb4b6e96fd72c658947e7b to your computer and use it in GitHub Desktop.

Select an option

Save R3V1Z3/8371822334eb4b6e96fd72c658947e7b to your computer and use it in GitHub Desktop.

Happy Birthday Destiny! ๐ŸŽ‚

A magical birthday animation with falling snow and an interactive present!

var presentOpened = false
var openProgress = 0.0
var confettiStartFrame = 0
clear()
clearFg()

# Magical gradient background (dark blue to purple)
var y = 0
while y < height:
  var ratio = float(y) / float(height)
  var r = int(20.0 + ratio * 40.0)
  var g = int(20.0 + ratio * 20.0)
  var b = int(60.0 + ratio * 80.0)
  setColor(r, g, b)
  fillRect(0, y, width, 1)
  y = y + 1

# Draw snowflakes (procedurally generated each frame)
var i = 0
while i < 80:
  var time = frameCount / 60.0
  var baseX = float(i * width) / 80.0
  var x = baseX + sin(time * 0.5 + float(i)) * 30.0
  var y = float((frameCount * (i mod 3 + 1) + i * 97) mod height)
  var size = 1.0 + float((i * 37) mod 10) / 10.0
  
  # Draw snowflake with slight glow
  setColor(255, 255, 255, 180)
  fillCircle(int(x), int(y), int(size))
  setColor(200, 220, 255, 100)
  fillCircle(int(x), int(y), int(size) + 1)
  i = i + 1

# Present position (center of screen)
var presentX = width / 2
var presentY = height / 2

# Get mouse position
var mouseX = getMouseX()
var mouseY = getMouseY()

# Check if present was clicked
var leftPressed = isMouseButtonPressed(0)
if leftPressed:
  var dx = mouseX - presentX
  var dy = mouseY - presentY
  var inBounds = dx > -60 and dx < 60 and dy > -60 and dy < 60
  if inBounds:
    if not presentOpened:
      presentOpened = true
      confettiStartFrame = frameCount

# Animate opening
if presentOpened:
  if openProgress < 1.0:
    openProgress = openProgress + 0.02
    if openProgress > 1.0:
      openProgress = 1.0

# Draw present
if openProgress < 1.0:
  # Present box (pink/magenta)
  setColor(255, 100, 180)
  fillRect(presentX - 55, presentY - 45, 110, 90)
  
  # Present ribbon (gold)
  setColor(255, 215, 0)
  fillRect(presentX - 55, presentY - 5, 110, 10)
  fillRect(presentX - 5, presentY - 45, 10, 90)
  
  # Present bow (animated)
  var bowBounce = sin(frameCount / 10.0) * 2.0
  setColor(255, 215, 0)
  fillCircle(presentX - 20, presentY + int(bowBounce), 12)
  fillCircle(presentX + 20, presentY + int(bowBounce), 12)
  fillCircle(presentX, presentY - 8 + int(bowBounce), 14)

# Opening animation
if presentOpened:
  var lidY = presentY - 40 - int(openProgress * 100.0)
  
  # Calculate fade out alpha (lid disappears as it opens)
  var lidAlpha = 255 - int(openProgress * 255.0)
  if lidAlpha < 0:
    lidAlpha = 0
  
  # Opening present lid (fades out)
  if lidAlpha > 0:
    setColor(255, 100, 180, lidAlpha)
    fillRect(presentX - 50, lidY, 100, 40)
    setColor(255, 215, 0, lidAlpha)
    fillRect(presentX - 55, lidY + 35, 110, 10)
  
  # Procedural confetti with physics
  var confettiTime = frameCount - confettiStartFrame
  if confettiTime < 200:
    var confettiI = 0
    while confettiI < 30:
      # Procedural physics calculation
      var angle = float(confettiI) * 0.209 + float(confettiI * 17) / 10.0
      var speed = 3.0 + float((confettiI * 13) mod 40) / 10.0
      var initialVX = cos(angle) * speed
      var initialVY = sin(angle) * speed - 4.0
      
      # Apply physics over time
      var t = float(confettiTime)
      var cx = float(presentX) + initialVX * t
      var cy = float(presentY) + initialVY * t + 0.15 * t * t / 2.0
      
      # Only draw if on screen
      var cxi = int(cx)
      var cyi = int(cy)
      if cyi < height and cxi > 0 and cxi < width and cyi > 0:
        var colorChoice = confettiI mod 5
        if colorChoice == 0:
          setColor(255, 100, 100)
        if colorChoice == 1:
          setColor(100, 255, 100)
        if colorChoice == 2:
          setColor(100, 100, 255)
        if colorChoice == 3:
          setColor(255, 255, 100)
        if colorChoice == 4:
          setColor(255, 100, 255)
        
        # Draw confetti piece
        var size = 4
        fillRect(cxi - size, cyi - size, size * 2, size * 2)
      
      confettiI = confettiI + 1
  
  # Birthday message appears
  if openProgress > 0.8:
    var messageAlpha = int((openProgress - 0.8) * 5.0 * 255.0)
    if messageAlpha > 255:
      messageAlpha = 255
    
    # Centered "Happy Birthday"
    setColor(255, 215, 0, messageAlpha)
    var happyText = "Happy Birthday"
    var happyWidth = 220  # Approximate width for size 32
    drawText(presentX - happyWidth / 2, presentY - 180, happyText, 32)
    
    # Centered "Destiny!"
    setColor(255, 100, 180, messageAlpha)
    var destinyText = "Destiny!"
    var destinyWidth = 140  # Approximate width for size 36
    drawText(presentX - destinyWidth / 2, presentY - 140, destinyText, 36)
    
    # Sparkles around text
    var sparkleI = 0
    while sparkleI < 12:
      var angle = float(sparkleI) * 0.524 + float(frameCount) / 20.0
      var dist = 160.0 + sin(float(frameCount + sparkleI * 10) / 10.0) * 20.0
      var sx = presentX + int(cos(angle) * dist)
      var sy = presentY - 150 + int(sin(angle) * dist)
      var sparkleSize = int(3.0 + sin(float(frameCount + sparkleI * 5) / 5.0) * 2.0)
      setColor(255, 255, 200, messageAlpha)
      fillCircle(sx, sy, sparkleSize)
      sparkleI = sparkleI + 1

# Instruction text (only before opening)
var notOpened = not presentOpened
if notOpened:
  var bounce = sin(frameCount / 20.0) * 5.0
  setColor(255, 255, 255)
  var instructionText = "Click to open!"
  var instructionWidth = 140  # Approximate width for size 24
  drawText(presentX - instructionWidth / 2, presentY + 150 + int(bounce), instructionText, 24)

# Bottom sparkle line
var sparkBottomI = 0
while sparkBottomI < 30:
  var sparkX = (sparkBottomI * width) / 30 + int(sin(float(frameCount + sparkBottomI * 10) / 10.0) * 10.0)
  var sparkY = height - 30 + int(sin(float(frameCount + sparkBottomI * 15) / 8.0) * 5.0)
  var brightness = int(sin(float(frameCount + sparkBottomI * 20) / 6.0) * 100.0 + 155.0)
  setColor(brightness, brightness, 255)
  # fillCircle(sparkX, sparkY, 2)
  sparkBottomI = sparkBottomI + 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment