0%

Every Love Is Unique

Golden Heart is a generative art experiment built entirely with Python, transforming thousands of points and connections into a glowing, beating golden heart. Using Pygame, NumPy, and SciPy, the piece combines mathematical geometry, procedural generation, and dynamic animation to create a living network of light.

This project explores how a simple mathematical idea can become a living visual. Instead of drawing a traditional heart, the program creates thousands of points inside a mathematical heart shape and connects them with randomly generated golden lines. As the structure builds, pulses, flashes, and fades, the result feels less like a static drawing and more like a small digital organism.

🕯️ Magic Note

Every heart is made from the same idea, but never in exactly the same way. A little randomness. A little geometry. A little code. And suddenly, something unique appears.

Project Overview
A generative golden heart created entirely with Python and Pygame.
  • Mathematical heart geometry
  • Thousands of randomly generated points
  • Random connections based on distance
  • Golden RGB color variations
  • Animated construction and fading
  • Subtle sinusoidal pulse movement
What You Will Build
The final animation is a constantly changing network of golden lines that gradually forms a heart, reaches its brightest moment, and then disappears.
  • 1. A generative heart built entirely with Python
  • 2. A Pygame window for real-time rendering
  • 3. Thousands of points distributed inside the heart
  • 4. Randomized connections between nearby points
  • 5. A varied golden palette and line thickness
  • 6. A smooth building, resting, flashing, and fading cycle
  • 7. A subtle pulse that makes the heart feel alive
How the Heart Is Made
The heart is not drawn as a conventional filled shape. Instead, the program uses mathematics to determine which randomly generated coordinates belong inside the heart.
1. The Heart Shape
A mathematical heart equation defines the boundary of the shape. Random coordinates are tested against the equation, and only the coordinates that satisfy the condition are kept. This gives the program a simple rule for generating a complex-looking shape without manually defining thousands of points.

🕯️ Magic Note

A mathematical equation can describe much more than numbers. With the right coordinates, an equation becomes a shape. With enough points, the shape becomes a world.

2. Generating the Points
Thousands of points are generated randomly within the heart’s area. Because the points are not placed on a rigid grid, every run can produce a slightly different internal structure. Different point distributions can also be used to make important areas denser and visually richer.
3. Building the Connections
Once the points exist, the program begins connecting them. For each point, nearby points are considered as possible connections. Distance influences the probability of creating a line, allowing close points to connect more easily while preventing the entire heart from becoming one dense mesh. This combination of distance and probability creates a network that feels organic rather than perfectly calculated.
4. Creating the Golden Palette
The heart uses several shades of gold instead of a single color. Each connection receives a randomly selected RGB value from the golden palette. Line thickness is also varied between one and three pixels. These small differences prevent the heart from looking flat and give the network more depth, texture, and visual movement.
ElementRole
PointsDefine the internal structure of the heart
ConnectionsCreate the glowing network
DistanceControls which points can connect
ProbabilityAdds randomness to the network
Golden paletteCreates the visual identity
Line thicknessAdds depth and variation
How It Comes Alive
The animation is divided into four simple states. Each state changes what happens on screen and then passes control to the next stage.
  • 1. Building: Connections appear progressively until the heart is complete.
  • 2. Resting: The completed heart remains visible for a short moment.
  • 3. Flashing: A brief increase in brightness highlights the finished structure.
  • 4. Fading: The heart gradually becomes transparent before the cycle starts again.
Pulse & Motion
The heart is not completely static after it has been created. A sinusoidal function produces a small, continuous scaling effect around the center of the heart. This creates a subtle breathing or heartbeat-like movement without changing the underlying geometry. The animation can also introduce small differences in line movement and brightness so that the entire structure feels less mechanical.

🕯️ Magic Note

The pulse does not need to be dramatic. A tiny movement, repeated at the right rhythm, is enough to make a static shape feel alive.

Requirements
SoftwareRequired
PythonYes
PygameYes
Additional dependenciespygame
Running the Project
Follow these steps to run the animation on your computer.
  • 1. Install Python on your computer.
  • 2. Install Pygame using pip.
  • 3. Create a new Python file.
  • 4. Paste the project code into the file.
  • 5. Run the Python file.

Python

import pygame

import math

import random

pygame.init()

WIDTH, HEIGHT = 800, 800

screen = pygame.display.set_mode((WIDTH, HEIGHT))

pygame.display.set_caption("EVERY LOVE IS UNIQUE")

clock = pygame.time.Clock()

BLACK = (0, 0, 0)

CENTER = (WIDTH // 2, HEIGHT // 2)

GOLD_WHITE = (255, 252, 238)

GOLD_HIGHLIGHT = (255, 246, 215)

GOLD_LIGHT = (255, 228, 140)

GOLD_LIGHT2 = (250, 210, 90)

GOLD = (235, 185, 55)

GOLD2 = (220, 170, 45)

GOLD_DARK = (185, 140, 35)

GOLD_DARK2 = (160, 120, 30)

GOLD_SHADOW = (125, 92, 28)

GOLD_SHADOW2 = (105, 78, 22)

GOLD_PALETTE = [

GOLD_WHITE, GOLD_HIGHLIGHT, GOLD_LIGHT, GOLD_LIGHT2, GOLD,

GOLD2, GOLD_DARK, GOLD_DARK2, GOLD_SHADOW, GOLD_SHADOW2

]

def is_inside_heart(x, y):

x_norm, y_norm = x / 16, y / 13

return (x_norm**2 + y_norm**2 - 1)**3 - x_norm**2 * y_norm**3 <= 0

def generate_heart_point():

while True:

x, y = random.uniform(-16, 16), random.uniform(-14, 14)

if is_inside_heart(x, y):

rand = random.random()

if rand < 0.50:

scale = random.uniform(0.92, 1.0)

elif rand < 0.75:

scale = random.uniform(0.7, 0.92)

elif rand < 0.90:

scale = random.uniform(0.4, 0.7)

else:

scale = random.uniform(0.1, 0.4)

if y > 2 and random.random() < 0.45:

if x > 0:

x = x * random.uniform(0.8, 0.95) + random.uniform(2, 4)

else:

x = x * random.uniform(0.8, 0.95) - random.uniform(2, 4)

scale *= random.uniform(0.8, 0.9)

if y < -6 and random.random() < 0.35:

scale *= random.uniform(0.92, 1.0)

x *= random.uniform(0.85, 0.95)

return x * scale, y * scale

points = []

num_points = 5500

for _ in range(num_points):

x, y = generate_heart_point()

px = CENTER[0] + x * 18 + random.uniform(-0.8, 0.8)

py = CENTER[1] - y * 18 + random.uniform(-0.8, 0.8)

points.append((px, py))

for t in range(350):

t_angle = (t / 350) * 2 * math.pi

x = 16 * math.sin(t_angle) ** 3

y = 13 * math.cos(t_angle) - 5 * math.cos(2 * t_angle) - 2 * math.cos(3 * t_angle) - math.cos(4 * t_angle)

noise_x, noise_y = random.uniform(-3, 3), random.uniform(-3, 3)

if y > 2:

noise_x, noise_y = random.uniform(-4, 4), random.uniform(-2, 2)

elif y < -6:

noise_x, noise_y = random.uniform(-1.5, 1.5), random.uniform(-1.5, 1.5)

px = CENTER[0] + x * 18 + noise_x

py = CENTER[1] - y * 18 + noise_y

points.append((px, py))

for _ in range(250):

x, y = random.uniform(-11, -3), random.uniform(3, 10)

if is_inside_heart(x, y):

px = CENTER[0] + x * 18 + random.uniform(-2, 2)

py = CENTER[1] - y * 18 + random.uniform(-1.5, 1.5)

points.append((px, py))

x, y = random.uniform(3, 11), random.uniform(3, 10)

if is_inside_heart(x, y):

px = CENTER[0] + x * 18 + random.uniform(-2, 2)

py = CENTER[1] - y * 18 + random.uniform(-1.5, 1.5)

points.append((px, py))

for _ in range(80):

x, y = random.uniform(-3, 3), random.uniform(6, 10)

if is_inside_heart(x, y):

px = CENTER[0] + x * 18 + random.uniform(-1, 1)

py = CENTER[1] - y * 18 + random.uniform(-2, 2)

points.append((px, py))

for _ in range(400):

x, y = random.uniform(-3.5, 3.5), random.uniform(-14, -7)

if is_inside_heart(x, y):

px = CENTER[0] + x * 18 + random.uniform(-0.4, 0.4)

py = CENTER[1] - y * 18 + random.uniform(-0.4, 0.4)

points.append((px, py))

for _ in range(150):

x, y = random.uniform(-1.2, 1.2), random.uniform(-14.8, -11.5)

if is_inside_heart(x, y):

px = CENTER[0] + x * 18 + random.uniform(-0.2, 0.2)

py = CENTER[1] - y * 18 + random.uniform(-0.2, 0.2)

points.append((px, py))

for _ in range(60):

x, y = random.uniform(-0.8, 0.8), random.uniform(-15, -13)

if is_inside_heart(x, y):

px = CENTER[0] + x * 18 + random.uniform(-0.1, 0.1)

py = CENTER[1] - y * 18 + random.uniform(-0.1, 0.1)

points.append((px, py))

lines = []

line_properties = []

max_attempts = 45000

attempts = 0

while len(lines) < 10000 and attempts < max_attempts:

attempts += 1

a, b = random.randrange(len(points)), random.randrange(len(points))

if a == b:

continue

x1, y1 = points[a]

x2, y2 = points[b]

d = math.hypot(x1 - x2, y1 - y2)

if d < 5:

continue

if d < 35 and random.random() < 0.35:

pass

elif d < 65 and random.random() < 0.40:

pass

elif d < 100 and random.random() < 0.25:

pass

else:

continue

lines.append((a, b))

color = random.choices(

GOLD_PALETTE,

weights=[10, 5, 15, 10, 20, 15, 10, 5, 5, 5],

k=1

)[0]

thickness = random.choices(

[1, 1, 2, 2, 3],

weights=[0.35, 0.30, 0.20, 0.10, 0.05]

)[0]

line_properties.append({'color': color, 'thickness': thickness})

progress = 0

phase = 'building'

flash_timer = 0

rest_timer = 0

fade_timer = 0

brightness_boost = 0

line_alpha = 1.0

running = True

try:

while running:

for event in pygame.event.get():

if event.type == pygame.QUIT:

running = False

if event.type == pygame.KEYDOWN:

if event.key == pygame.K_ESCAPE:

running = False

if event.key == pygame.K_r:

progress = 0

phase = 'building'

flash_timer = 0

rest_timer = 0

fade_timer = 0

brightness_boost = 0

line_alpha = 1.0

screen.fill(BLACK)

time_ms = pygame.time.get_ticks()

beat = 1 + 0.025 * math.sin(time_ms * 0.0038)

if phase == 'building':

progress += 12

if progress >= len(lines):

progress = len(lines)

phase = 'resting'

rest_timer = 0

line_alpha = 1.0

elif phase == 'resting':

rest_timer += 1

if rest_timer > 120:

phase = 'flashing'

flash_timer = 0

elif phase == 'flashing':

flash_timer += 1

if flash_timer < 15:

brightness_boost = 0.4 * (1 - flash_timer / 15)

else:

brightness_boost = 0

phase = 'fading'

fade_timer = 0

elif phase == 'fading':

fade_timer += 1

if fade_timer < 60:

line_alpha = 1.0 - fade_timer / 60

else:

line_alpha = 0

progress = 0

phase = 'building'

line_alpha = 1.0

brightness_boost = 0

draw_count = min(progress, len(lines))

for i in range(draw_count):

a, b = lines[i]

props = line_properties[i]

x1, y1 = points[a]

x2, y2 = points[b]

individual_beat = 1 + 0.012 * math.sin(

time_ms * 0.0038 + i * 0.001

)

current_beat = beat * individual_beat

cx1 = CENTER[0] + (x1 - CENTER[0]) * current_beat

cy1 = CENTER[1] + (y1 - CENTER[1]) * current_beat

cx2 = CENTER[0] + (x2 - CENTER[0]) * current_beat

cy2 = CENTER[1] + (y2 - CENTER[1]) * current_beat

flash_brightness = 1 + brightness_boost

beat_brightness = 0.9 + 0.15 * (current_beat - 0.975) * 8

total_brightness = flash_brightness * beat_brightness * line_alpha

r, g, b = props['color']

r = min(255, max(30, int(r * total_brightness)))

g = min(255, max(30, int(g * total_brightness)))

b = min(255, max(30, int(b * total_brightness)))

if props['thickness'] == 1:

pygame.draw.aaline(

screen, (r, g, b), (cx1, cy1), (cx2, cy2)

)

else:

pygame.draw.line(

screen,

(r, g, b),

(cx1, cy1),

(cx2, cy2),

props['thickness']

)

if phase == 'building':

glow = progress - 130

if 0 < glow < draw_count and line_alpha > 0.1:

glow_radius = 55

for i in range(

max(0, glow - glow_radius),

min(draw_count, glow + glow_radius)

):

a, b = lines[i]

x1, y1 = points[a]

x2, y2 = points[b]

current_beat = beat

cx1 = CENTER[0] + (x1 - CENTER[0]) * current_beat

cy1 = CENTER[1] + (y1 - CENTER[1]) * current_beat

cx2 = CENTER[0] + (x2 - CENTER[0]) * current_beat

cy2 = CENTER[1] + (y2 - CENTER[1]) * current_beat

distance = abs(i - glow)

alpha = max(

0,

int(200 * (1 - distance / glow_radius) * line_alpha)

)

if alpha > 0:

if alpha > 150:

glow_color = (255, 252, 238)

elif alpha > 80:

glow_color = (255, 248, 225)

else:

glow_color = (255, 240, 200)

glow_width = (

3 if alpha > 150

else 2 if alpha > 80

else 1

)

pygame.draw.line(

screen,

glow_color,

(cx1, cy1),

(cx2, cy2),

glow_width

)

pygame.display.flip()

clock.tick(60)

finally:

pygame.quit()

Experiment With It
The interesting part of generative art is that small changes to the rules can completely change the result. Try modifying the parameters and observe how the personality of the heart changes.
  • Increase or decrease the number of points
  • Change the number of possible connections
  • Adjust the heart scale
  • Create your own golden color palette
  • Change the line thickness
  • Make the pulse faster or slower
  • Change the building speed
  • Increase or decrease the resting duration
  • Experiment with flashing intensity
  • Change the fading speed

🕯️ Magic Note

Do not try to make every version perfect. Change one rule. Run it again. Watch what happens. Generative art becomes interesting when you stop controlling every detail.

What This Project Teaches
  • Working with mathematical shapes
  • Generating points with randomness
  • Filtering coordinates using mathematical conditions
  • Connecting objects based on distance
  • Using probability to control visual behavior
  • Working with RGB colors
  • Creating animation with time and sine waves
  • Managing multiple animation states
  • Building generative visuals with Python
  • Turning simple rules into complex visual results

⚡ Whisper

Code does not always need to solve a practical problem. Sometimes, it can simply create something that makes you stop and look. This heart is only geometry, randomness, color, motion, and a few simple rules. But when those rules work together, something unexpected appears. That is one of the beautiful things about creative coding. You write the rules. The computer creates the variation. And somewhere between the two, something unique begins to exist.