SnIPPETsX

SnIPPETsX › WIDTH = 800

WIDTH = 800

Python · updated Oct 14, 2026 · 353 lines

main.js

import tkinter as tk
import random
import math
WIDTH = 800
HEIGHT = 500
root = tk.Tk()
root.title("✨")
root.geometry(f"{WIDTH}x{HEIGHT}")
root.resizable(False, False)
root.configure(bg="black")
canvas = tk.Canvas(
    root,
    width=WIDTH,
    height=HEIGHT,
    bg="black",
    highlightthickness=0
)
canvas.pack()

WHITE = "#ffffff"
RED = "#d71920"
BLACK = "#050505"
GRAY = "#777777"
particles = []
for _ in range(70):

    x = random.randint(0, WIDTH)
    y = random.randint(0, HEIGHT)
    size = random.choice([1, 1, 2])
    particle = canvas.create_rectangle(
        x, y,
        x + size,
        y + size,
        fill=random.choice([WHITE, GRAY, "#777777"]),
        outline=""
    )
    particles.append({
        "id": particle,
        "x": x,
        "y": y,
        "speed": random.uniform(0.2, 0.7)
    })
mask = canvas.create_oval(
    0, 0, 0, 0,
    fill=RED,
    outline=BLACK,
    width=4
)
web_lines = []
web_lines.append(
    canvas.create_line(
        0, 0, 0, 0,
        fill=BLACK,
        width=1
    )
)
for _ in range(6):

    web_lines.append(
        canvas.create_line(
            0, 0, 0, 0,
            fill=BLACK,
            width=1
        )
    )
horizontal_webs = []
for _ in range(4):
    horizontal_webs.append(
        canvas.create_line(
            0, 0, 0, 0,
            fill=BLACK,
            width=1
        )
    )
left_eye = canvas.create_polygon(
    0, 0, 0, 0,
    fill=WHITE,
    outline=BLACK,
    width=3
)
right_eye = canvas.create_polygon(
    0, 0, 0, 0,
    fill=WHITE,
    outline=BLACK,
    width=3
)
thread = canvas.create_line(
    400,
    0,
    400,
    100,
    fill=WHITE,
    width=1
)
def rotate_point(x, y, angle):

    cos_a = math.cos(angle)
    sin_a = math.sin(angle)
    return (
        x * cos_a - y * sin_a,
        x * sin_a + y * cos_a
    )
angle = 0
direction = 1
def animate_mask():
    global angle, direction
    angle += 0.018 * direction
    if angle > 0.30:
        direction = -1
    if angle < -0.30:
        direction = 1
    pivot_x = 400
    pivot_y = 30
    rope_length = 125
    mask_x = pivot_x + math.sin(angle) * rope_length
    mask_y = pivot_y + math.cos(angle) * rope_length
    canvas.coords(
        thread,
        pivot_x,
        pivot_y,
        mask_x,
        mask_y - 58
    )
    canvas.coords(
        mask,
        mask_x - 45,
        mask_y - 60,
        mask_x + 45,
        mask_y + 60
    )
    p1 = rotate_point(0, -58, angle)
    p2 = rotate_point(0, 58, angle)
    canvas.coords(
        web_lines[0],
        mask_x + p1[0],
        mask_y + p1[1],
        mask_x + p2[0],
        mask_y + p2[1]
    )
    radial_angles = [
        -65,
        -40,
        -20,
        20,
        40,
        65
    ]

    for i, degrees in enumerate(radial_angles):

        rad = math.radians(degrees)

        x1 = math.sin(rad) * 5
        y1 = -math.cos(rad) * 5

        x2 = math.sin(rad) * 43
        y2 = -math.cos(rad) * 62

        p1 = rotate_point(x1, y1, angle)
        p2 = rotate_point(x2, y2, angle)

        canvas.coords(
            web_lines[i + 1],
            mask_x + p1[0],
            mask_y + p1[1],
            mask_x + p2[0],
            mask_y + p2[1]
        )



    web_y_positions = [
        -38,
        -15,
        12,
        38
    ]

    for i, y in enumerate(web_y_positions):

        width = 30 + (abs(y) * 0.20)

        p1 = rotate_point(-width, y, angle)
        p2 = rotate_point(width, y, angle)

        canvas.coords(
            horizontal_webs[i],
            mask_x + p1[0],
            mask_y + p1[1],
            mask_x + p2[0],
            mask_y + p2[1]
        )




    left_eye_points = [
        (-37, -20),
        (-12, -39),
        (-17, -4),
        (-35, 12)
    ]

    right_eye_points = [
        (37, -20),
        (12, -39),
        (17, -4),
        (35, 12)
    ]


    def transform_polygon(points):

        result = []

        for x, y in points:

            rx, ry = rotate_point(x, y, angle)

            result.extend([
                mask_x + rx,
                mask_y + ry
            ])

        return result


    canvas.coords(
        left_eye,
        *transform_polygon(left_eye_points)
    )

    canvas.coords(
        right_eye,
        *transform_polygon(right_eye_points)
    )


    root.after(30, animate_mask)



title = canvas.create_text(
    400,
    285,
    text="",
    fill=WHITE,
    font=("Courier New", 34, "bold")
)


NAME = "! PRERNA !"
name_text = canvas.create_text(
    400,
    335,
    text="",
    fill="#00ff66",
    font=("Courier New", 18, "bold")
)



message = "HAPPY BIRTHDAY!"
index = 0


def type_message():

    global index

    if index <= len(message):

        canvas.itemconfig(
            title,
            text=message[:index]
        )

        index += 1

        root.after(130, type_message)

    else:

        # Wait a tiny bit before showing the name
        root.after(500, show_name)



def show_name():

    canvas.itemconfig(
        name_text,
        text=NAME
    )

    pulse()


pulse_direction = 1
pulse_size = 34


def pulse():

    global pulse_direction, pulse_size

    pulse_size += pulse_direction * 0.4

    if pulse_size >= 38:
        pulse_direction = -1

    if pulse_size <= 34:
        pulse_direction = 1

    canvas.itemconfig(
        title,
        font=("Courier New", int(pulse_size), "bold")
    )

    root.after(40, pulse)



def animate_particles():

    for p in particles:

        p["y"] -= p["speed"]

        if p["y"] < 0:

            p["y"] = HEIGHT
            p["x"] = random.randint(0, WIDTH)

        size = random.choice([1, 1, 2])

        canvas.coords(
            p["id"],
            p["x"],
            p["y"],
            p["x"] + size,
            p["y"] + size
        )

    root.after(30, animate_particles)


animate_particles()
animate_mask()

root.after(1200, type_message)

root.mainloop()

Run this Python snippet

Open this snippet on SnIPPETsX to run the Python code in your browser, edit it together with others in real time, or fork it into your own copy. No install or account is needed.