Full Wave Bridge Rectifier Simulation with Pygame
Educational Python Project – Visualize AC to DC Conversion
This tutorial walks through building a Full Wave Bridge Rectifier Simulation using Python and Pygame. The simulation visually demonstrates how AC (Alternating Current) is converted into DC (Direct Current) using four diodes arranged in a bridge circuit.
Table of Contents
- Overview
- Circuit Concept
- Pygame Setup
- Drawing the Circuit
- Waveform Animation
- Current Flow Visualization
- Complete Python Code
- How to Run
- Key Learnings
- Further Ideas
Overview
This simulation provides an interactive, animated look at how a bridge rectifier converts alternating current into pulsating direct current. The display shows:
- Top: Input AC waveform
- Center: Bridge circuit with diode conduction paths
- Bottom: Output DC waveform
The current flow alternates between two paths during positive and negative half-cycles, visually explaining the rectification process.
Circuit Concept
A full wave bridge rectifier uses four diodes (D1–D4) arranged in a diamond shape. During each half-cycle:
- Positive Half-Cycle:
- Current flows through D1 → Load → D4.
- D1 and D4 conduct.
- Negative Half-Cycle:
- Current flows through D3 → Load → D2.
- D2 and D3 conduct.
Thus, both halves of the AC waveform are converted into unidirectional current, producing a full-wave DC output.
Pygame Setup
We start by setting up a 650×900 portrait window to allow space for the waveforms and the circuit diagram.
import pygame, sys, math
pygame.init()
W, H = 650, 900
S = pygame.display.set_mode((W, H))
pygame.display.set_caption("Full Wave Bridge Rectifier")
F = pygame.font.SysFont("Arial", 18, bold=True)
clk = pygame.time.Clock()
We define a dictionary of colors for background, grid, wires, diodes, and active paths:
C = {
"BG": (0, 0, 0),
"GRID": (50, 50, 50),
"WIRE": (100, 100, 100),
"DIODE": (150, 150, 150),
"IN": (0, 255, 255),
"OUT": (255, 220, 0),
"ANODE": (0, 255, 0),
"CATHODE": (255, 0, 0),
"POSITIVE_HALF": (50, 50, 255),
"NEGATIVE_HALF": (255, 50, 50)
}
Drawing the Circuit
We build a diamond-shaped layout for the diodes:
(D1)
/ \
D2 D3
\ /
(D4)
Each diode is drawn with proper anode/cathode markers using pygame.draw.polygon() and pygame.draw.circle() for clarity.
Active diodes (the ones conducting in the current half-cycle) are highlighted in bright colors.
Waveform Animation
Two waveforms are drawn to represent input AC and output DC signals:
- The input AC is plotted as a sine wave (
math.sin()), color-coded for positive (blue) and negative (red) halves. - The output DC is plotted as the absolute value of the sine wave, representing rectified output.
for x in range(280):
y_val = amp * math.sin(freq*(x + p))
y_pos = box_y + y_val
color = C["POSITIVE_HALF"] if y_val >= 0 else C["NEGATIVE_HALF"]
draw.circle(S, color, (cx - 140 + x, int(y_pos)), 1)
Current Flow Visualization
To make the current path more interactive, small moving dots simulate the direction of electron flow through the active diodes and load resistor. The animation updates continuously using a changing phase variable.
Positive and negative half-cycles each show distinct paths:
- Blue dots: Positive half (D1–D4 conducting)
- Red dots: Negative half (D2–D3 conducting)
This helps students visually understand how current always flows in the same direction through the load.
Complete Python Code
Below is the complete code that combines everything explained above.
import pygame, sys, math
pygame.init()
## Setup
W, H = 650, 900 # Portrait orientation
S = pygame.display.set_mode((W, H))
pygame.display.set_caption("Full Wave Bridge Rectifier")
F = pygame.font.SysFont("Arial", 18, bold=True)
clk = pygame.time.Clock()
## Colors
C = {
"BG": (0, 0, 0),
"GRID": (50, 50, 50),
"WIRE": (100, 100, 100), # Dimmer for non-active wires
"DIODE": (150, 150, 150), # Dimmer for non-active diodes
"IN": (0, 255, 255),
"OUT": (255, 220, 0),
"LOAD": (255, 150, 150),
"ANODE": (0, 255, 0), # Green for anode
"CATHODE": (255, 0, 0), # Red for cathode
"POSITIVE_HALF": (50, 50, 255), # BLUE for positive half (swapped)
"NEGATIVE_HALF": (255, 50, 50) # RED for negative half (swapped)
}
## Wave and Animation
amp, freq, speed, phase = 50, 0.15, .04, 0
draw = pygame.draw
txt = lambda t, p, c: S.blit(F.render(t, 1, c),
F.render(t, 1, c).get_rect(center=p))
def grid():
for x in range(0, W, 50):
draw.line(S, C["GRID"], (x, 0), (x, H))
for y in range(0, H, 50):
draw.line(S, C["GRID"], (0, y), (W, y))
def draw_diode_with_polarity(pos, angle=0, color=None, active=False, reverse=False):
x, y = pos
s = 20
## Create a surface for the diode
diode_surface = pygame.Surface((s*3, s*3), pygame.SRCALPHA)
## Use provided color or default
if active and color:
diode_color = color
wire_color = color
## Make active diodes brighter
anode_color = (100, 255, 100) # Brighter green
cathode_color = (255, 100, 100) # Brighter red
else:
diode_color = C["DIODE"]
wire_color = C["WIRE"]
anode_color = C["ANODE"]
cathode_color = C["CATHODE"]
## Draw diode symbol - reverse the direction if needed
if reverse:
## Reverse diode (cathode on left, anode on right)
pygame.draw.polygon(diode_surface, diode_color,
[(s*2.2, s*0.5), (s*2.2, s*2.5), (s*1.2, s*1.5)])
pygame.draw.polygon(diode_surface, wire_color,
[(s*2.2, s*0.5), (s*2.2, s*2.5), (s*1.2, s*1.5)], 2)
pygame.draw.line(diode_surface, wire_color, (s*0.8, s*0.5), (s*0.8, s*2.5), 3)
## Anode and cathode markers (swapped for reverse diode)
pygame.draw.circle(diode_surface, cathode_color, (int(s*0.5), int(s*1.5)), 4) # Cathode on left
pygame.draw.circle(diode_surface, anode_color, (int(s*2.5), int(s*1.5)), 4) # Anode on right
else:
## Normal diode (anode on left, cathode on right)
pygame.draw.polygon(diode_surface, diode_color,
[(s*0.8, s*0.5), (s*0.8, s*2.5), (s*1.8, s*1.5)])
pygame.draw.polygon(diode_surface, wire_color,
[(s*0.8, s*0.5), (s*0.8, s*2.5), (s*1.8, s*1.5)], 2)
pygame.draw.line(diode_surface, wire_color, (s*2.2, s*0.5), (s*2.2, s*2.5), 3)
## Anode and cathode markers
pygame.draw.circle(diode_surface, anode_color, (int(s*0.5), int(s*1.5)), 4)
pygame.draw.circle(diode_surface, cathode_color, (int(s*2.5), int(s*1.5)), 4)
## Rotate the surface
rotated_surface = pygame.transform.rotate(diode_surface, angle)
## Get the rect and center it at the position
rect = rotated_surface.get_rect(center=(x, y))
S.blit(rotated_surface, rect)
def waves(p):
grid()
cx, cy = W//2, H//2
## Input Wave (AC) - TOP (moved 40px down)
box_y = 160 # Was 120, now 160 (40px down)
draw.rect(S, C["DIODE"], (cx - 150, box_y - 55, 300, 115), 2)
## Draw input wave with color coding
for x in range(280):
y_val = amp * math.sin(freq*(x + p))
y_pos = box_y + y_val
color = C["POSITIVE_HALF"] if y_val >= 0 else C["NEGATIVE_HALF"]
draw.circle(S, color, (cx - 140 + x, int(y_pos)), 1)
txt("AC Input Waveform", (cx, box_y - 90), C["IN"])
## txt("Live", (cx - 50, box_y + 80), C["IN"])
## txt("Neutral", (cx + 50, box_y + 80), C["IN"])
## Output Wave (Full-Wave DC) - BOTTOM (moved 40px up)
box_y = H - 160 # Was H - 120, now H - 160 (40px up)
draw.rect(S, C["DIODE"], (cx - 150, box_y - 70, 300, 115), 2)
## Draw output wave with color coding based on original input phase
for x in range(280):
y_val = amp * math.sin(freq*(x + p))
y_pos = box_y - amp * abs(math.sin(freq*(x + p)))
color = C["POSITIVE_HALF"] if y_val >= 0 else C["NEGATIVE_HALF"]
draw.circle(S, color, (cx - 140 + x, int(y_pos)), 1)
txt("DC Output Waveform", (cx, box_y - 90), C["OUT"])
txt("-", (cx - 50, box_y + 80), C["OUT"])
txt("+", (cx + 50, box_y + 80), C["OUT"])
## Bridge Rectifier Circuit - CENTER
center_x, center_y = cx, H//2
radius = 120
## Define the four points of the diamond
top = (center_x, center_y - radius) # Live input
right = (center_x + radius, center_y) # Positive output
bottom = (center_x, center_y + radius) # Neutral input
left = (center_x - radius, center_y) # Negative output
## Determine current half cycle for coloring
current_sine = math.sin(freq * (140 + p)) # Sample middle of waveform
is_positive_half = current_sine >= 0
active_color = C["POSITIVE_HALF"] if is_positive_half else C["NEGATIVE_HALF"]
## Draw all wires in dim color first
draw.line(S, C["WIRE"], top, right, 3) # Top to right
draw.line(S, C["WIRE"], right, bottom, 3) # Right to bottom
draw.line(S, C["WIRE"], bottom, left, 3) # Bottom to left
draw.line(S, C["WIRE"], left, top, 3) # Left to top
## Highlight active current paths with Z-shape
if is_positive_half:
## Positive half cycle: Live → D1 → Positive → Load → Negative → D4 → Neutral
## This forms a Z-shape: top-right to bottom-left
draw.line(S, active_color, top, right, 5) # Live to Positive via D1
draw.line(S, active_color, left, bottom, 5) # Negative to Neutral via D4
else:
## Negative half cycle: Neutral → D3 → Positive → Load → Negative → D2 → Live
## This forms the opposite Z-shape: bottom-right to top-left
draw.line(S, active_color, bottom, right, 5) # Neutral to Positive via D3
draw.line(S, active_color, left, top, 5) # Negative to Live via D2
## Diodes with UPDATED directions (D2 and D3 reversed):
## D1: Top-right - Live to Positive (normal direction)
## D2: Top-left - Negative to Live (REVERSE direction - NOW NORMAL)
## D3: Bottom-right - Neutral to Positive (normal direction - NOW REVERSE)
## D4: Bottom-left - Negative to Neutral (REVERSE direction)
## Only color the two conducting diodes
if is_positive_half:
## Positive half cycle: D1 and D4 conduct (ONLY these two are colored)
draw_diode_with_polarity(((top[0] + right[0])/2, (top[1] + right[1])/2), angle=135, color=active_color, active=True) # D1 (normal)
draw_diode_with_polarity(((left[0] + top[0])/2, (left[1] + top[1])/2), angle=225) # D2 (inactive - FIXED: removed color)
draw_diode_with_polarity(((right[0] + bottom[0])/2, (right[1] + bottom[1])/2), angle=45, reverse=True) # D3 (now reverse - was normal)
draw_diode_with_polarity(((bottom[0] + left[0])/2, (bottom[1] + left[1])/2), angle=315, color=active_color, active=True, reverse=True) # D4 (reverse)
else:
## Negative half cycle: D2 and D3 conduct (ONLY these two are colored)
draw_diode_with_polarity(((top[0] + right[0])/2, (top[1] + right[1])/2), angle=135) # D1 (inactive)
draw_diode_with_polarity(((left[0] + top[0])/2, (left[1] + top[1])/2), angle=225, color=active_color, active=True) # D2 (now normal - was reverse)
draw_diode_with_polarity(((right[0] + bottom[0])/2, (right[1] + bottom[1])/2), angle=45, color=active_color, active=True, reverse=True) # D3 (now reverse - was normal)
draw_diode_with_polarity(((bottom[0] + left[0])/2, (bottom[1] + left[1])/2), angle=315, reverse=True) # D4 (inactive)
## Label diodes
txt("D1", (center_x + radius*0.7, center_y - radius*0.3), C["WIRE"])
txt("D2", (center_x - radius*0.7, center_y - radius*0.3), C["WIRE"])
txt("D3", (center_x + radius*0.7, center_y + radius*0.3), C["WIRE"])
txt("D4", (center_x - radius*0.7, center_y + radius*0.3), C["WIRE"])
## AC Input connections - SHORT 20px wires with labels
## Live wire - short line upward from top point
wire_color_live = active_color if (is_positive_half) else C["WIRE"]
draw.line(S, wire_color_live, top, (top[0], top[1] - 20), 5 if is_positive_half else 3)
draw.circle(S, C["IN"], (top[0], top[1] - 25), 6, 2)
txt("L", (top[0], top[1] - 40), C["IN"])
## Neutral wire - short line downward from bottom point
wire_color_neutral = active_color if (not is_positive_half) else C["WIRE"]
draw.line(S, wire_color_neutral, bottom, (bottom[0], bottom[1] + 20), 5 if not is_positive_half else 3)
draw.line(S, C["IN"], (bottom[0] - 5, bottom[1] + 25), (bottom[0] + 5, bottom[1] + 25), 2)
txt("N", (bottom[0], bottom[1] + 40), C["IN"])
## DC Output connections - SHORT 20px wires with labels
## Positive output - short line to right from right point
draw.line(S, active_color, right, (right[0] + 20, right[1]), 5)
txt("-", (right[0] + 35, right[1]), C["OUT"])
## Negative output - short line to left from left point
draw.line(S, active_color, left, (left[0] - 20, left[1]), 5)
txt("+", (left[0] - 35, left[1]), C["OUT"])
## Load resistor - placed INSIDE bridge width
load_width, load_height = 60, 40
load_x, load_y = center_x, center_y
## Draw load resistor with active color
draw.rect(S, active_color, (load_x - load_width//2, load_y - load_height//2,
load_width, load_height), 3)
txt("Load", (load_x, load_y), active_color)
## Connect load to bridge - short connections with active color
draw.line(S, active_color, (right[0] - 3, right[1]),
(load_x + load_width//2, load_y), 5)
draw.line(S, active_color, (left[0] + 3, left[1]),
(load_x - load_width//2, load_y), 5)
## Add current flow animation (small moving dots) - REVERSED DIRECTION
dot_phase = (p * 10) % 20
dot_size = 6
if is_positive_half:
## Positive half current flow: Live → D1 → Positive → Load → Negative → D4 → Neutral
if dot_phase < 10:
## First segment: Live to Positive via D1 (REVERSED: now from Positive to Live)
progress = 1 - (dot_phase / 10) # Reverse progress
dot_x = top[0] + (right[0] - top[0]) * progress
dot_y = top[1] + (right[1] - top[1]) * progress
draw.circle(S, active_color, (int(dot_x), int(dot_y)), dot_size)
else:
## Second segment: Negative to Neutral via D4 (REVERSED: now from Neutral to Negative)
progress = 1 - ((dot_phase - 10) / 10) # Reverse progress
dot_x = left[0] + (bottom[0] - left[0]) * progress
dot_y = left[1] + (bottom[1] - left[1]) * progress
draw.circle(S, active_color, (int(dot_x), int(dot_y)), dot_size)
else:
## Negative half current flow: Neutral → D3 → Positive → Load → Negative → D2 → Live
if dot_phase < 10:
## First segment: Neutral to Positive via D3 (REVERSED: now from Positive to Neutral)
progress = 1 - (dot_phase / 10) # Reverse progress
dot_x = bottom[0] + (right[0] - bottom[0]) * progress
dot_y = bottom[1] + (right[1] - bottom[1]) * progress
draw.circle(S, active_color, (int(dot_x), int(dot_y)), dot_size)
else:
## Second segment: Negative to Live via D2 (REVERSED: now from Live to Negative)
progress = 1 - ((dot_phase - 10) / 10) # Reverse progress
dot_x = left[0] + (top[0] - left[0]) * progress
dot_y = left[1] + (top[1] - left[1]) * progress
draw.circle(S, active_color, (int(dot_x), int(dot_y)), dot_size)
## Add title and description
txt("Full Wave Bridge Rectifier", (cx, 50), C["WIRE"])
txt("AC to DC Conversion", (cx, 90), (200, 200, 200))
## Add connection indicators (adjusted positions)
txt("AC Input", (cx, center_y - radius - 60), C["IN"])
txt("DC Output", (cx, center_y + radius + 60), C["OUT"])
## Show current path description
if is_positive_half:
path_text = "Current Path (Blue): Live → D1 → (+) → Load → (-) → D4 → Neutral"
else:
path_text = "Current Path (Red): Neutral → D3 → (+) → Load → (-) → D2 → Live"
txt(path_text, (cx, H - 50), active_color)
## Main Loop
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT or \
(e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE):
pygame.quit(); sys.exit()
S.fill(C["BG"])
waves(phase)
phase += speed
pygame.display.flip()
clk.tick(60)
How to Run
- Install Pygame if you haven’t already:
pip install pygame - Save the script as
rectifier_sim.py. - Run it:
python rectifier_sim.py - Press [ESC] to quit.
Key Learnings
- How a Full Wave Bridge Rectifier works.
- How to simulate electric circuits visually using Python.
- Using Pygame for animation and dynamic drawing.
- Representing waveforms and logic through geometry and color.
Further Ideas
Here are some fun ways to extend the project:
- Add a filter capacitor to smooth the output waveform.
- Display a moving average of the output voltage.
- Add sound effects when current changes direction.
- Show voltage vs time graph side-by-side with the circuit.
Credits
Developed by PyShine — bringing electronics and Python to life through visual learning!
If you found this helpful, consider subscribing to PyShine on YouTube for more fun Python projects.
Happy Coding & Keep Learning! ⚡