Skip to content

Honeycomb Grid

By the end of this lab you'll be able to:

  • Write a hexagon(x, y, size) function and call it from nested loops
  • Understand the offset row pattern for tiling hexagons
  • See why hexagons tile the plane perfectly while squares leave gaps

Dozens of filled hexagons tile the canvas in alternating yellow and orange — a perfect honeycomb just like a beehive.

Welcome to the Honeycomb!

Monty waving welcome Hexagons are special: they tile the plane perfectly with no gaps or overlaps. In this lab you'll discover how to place them using a neat offset-row trick. Let's code it together!

How the Tiling Works

Each hexagon is drawn by a function. The key to tiling is offset rows: - Odd rows shift right by half a hexagon width - The vertical spacing between rows is the hexagon's height × 0.75

The hexagon height is size * 2 (vertex-to-vertex) and width is size * sqrt(3) (flat-to-flat, approximately size * 1.732).

Sample Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import turtle
import math
monty = turtle.Turtle()
monty.speed(0)
monty.hideturtle()

def hexagon(x, y, size, color):
    monty.penup()
    monty.goto(x, y)
    monty.setheading(30)
    monty.pendown()
    monty.color(color)
    monty.begin_fill()
    for _ in range(6):
        monty.forward(size)
        monty.left(60)
    monty.end_fill()

size = 30
colors = ['gold', 'orange']
h = size * 2                     # height of a pointy-top hexagon
col_w = size * math.sqrt(3)      # width of a pointy-top hexagon

for row in range(7):
    for col in range(8):
        offset = col_w / 2 if row % 2 == 1 else 0
        x = -200 + col * col_w + offset
        y = 150 - row * h * 0.75
        hexagon(x, y, size, colors[(row + col) % 2])

What Do You Think Will Happen?

Monty thinking A hexagon has 6 sides. The exterior angle at each corner is 360 / 6 = 60°. After drawing all 6 sides, how many total degrees has Monty turned? Make your guess — then click Run to find out!

Try It Now


  

6 × 60° = 360° — a full rotation, just like every regular polygon. Were you right?

How It Works

The hexagon() function uses setheading(30) so each hexagon starts at a 30° angle, producing a pointy-top hexagon. Resetting the heading at the start of every call is essential — you never know what angle Monty is at when the function is called.

The offset formula col_w / 2 if row % 2 == 1 else 0 shifts odd rows right by half the hex width, staggering them so each hexagon nestles into the gap between two hexagons in the row above.

Explanation Table

Line What it does
def hexagon(x, y, size, color) Reusable function: draws one hexagon at position (x,y)
monty.setheading(30) Start at 30° to draw a pointy-top hexagon — critical for correct tiling
monty.left(60) Exterior angle of a hexagon (360/6)
h = size * 2 Height of a pointy-top hexagon (vertex to vertex)
col_w = size * math.sqrt(3) Width of a pointy-top hexagon (flat edge to flat edge)
offset = col_w / 2 if row % 2 == 1 else 0 Odd rows shift right by half the hex width
y = 150 - row * h * 0.75 Rows overlap by 25% of height so hexagons interlock

Learning Check

Spot the Bug!

Monty warning The program below draws hexagons but they don't tile — there are gaps and overlaps! The row offset formula is wrong. Fix it so odd rows shift by exactly size pixels.


  

Change col_w to col_w / 2 in the offset line — the shift should be half the hex width, not the full width.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import math
import turtle


def draw_hexagon(size):
    """Draws a single regular hexagon."""
    for _ in range(6):
        turtle.forward(size)
        turtle.left(60)


def draw_honeycomb(rows, cols, size):
    """Draws a grid of hexagons offset to form a honeycomb pattern."""
    # Calculate geometric spacing based on hexagon properties
    w = size * math.sqrt(3)  # Width of the hexagon
    h = size * 1.5  # Vertical distance to the next row

    for r in range(rows):
        for c in range(cols):
            # Calculate the starting X position
            x = c * w
            # Offset every odd row to the right by half a hexagon width
            if r % 2 == 1:
                x += w / 2

            # Calculate the starting Y position
            y = r * h

            # Move turtle to the cell position without drawing
            turtle.penup()
            turtle.goto(x, y)
            turtle.pendown()

            # Render the hexagon
            draw_hexagon(size)


# Main setup
if __name__ == "__main__":
    # Optimize drawing speed
    turtle.speed(0)
    turtle.delay(0)

    # Configuration: Rows, Columns, Edge Size
    draw_honeycomb(rows=6, cols=8, size=30)

    # Keep window open
    turtle.hideturtle()
    turtle.done()

How the Math WorksThe Offset:

Hexagons cannot sit directly on top of each other in a standard grid. By using if r % 2 == 1: x += w / 2, we shift every odd row slightly to the right so it nests perfectly inside the valleys of the even row.Vertical Distance: The vertical step size (size * 1.5) ensures that rows overlap perfectly without crashing into each other or leaving spaces.

Alternative Package Options

If you are building a specific type of application, specialized libraries can save you from writing the grid math manually:

For Games & Simulation

Use the Red Blob Games Hex Grid Guide coordinate logic, or use the hexy library to manage neighbor tracking and axial coordinates easily.For Data Visualization: Use matplotlib.pyplot.hexbin() to quickly turn data coordinates into a beautiful hexagonal heatmap layout.For GIS & Maps: Look into h3-pandas or geohexgrid to map geographic coordinates directly onto a global honeycomb grid.

Experiments

  1. Change the colors. Use ['royalblue', 'navy'] for a blue honeycomb. You'll know it worked when the colors change but the tiling stays perfect.

  2. Make the hexagons bigger. Change size = 30 to size = 45 and reduce the row/col ranges. You'll know it worked when fewer, larger hexagons tile the canvas.

  3. Remove the fill. Delete the begin_fill() and end_fill() lines from the function. You'll know it worked when the honeycomb shows only outlines.

  4. Color by row only. Change colors[(row + col) % 2] to colors[row % 2]. All cells in a row become the same color. You'll know it worked when horizontal bands of alternating color appear.

Incredible Work!

Monty celebrating You built a full tiling engine with a function, nested loops, and offset-row geometry! Bees figured out this pattern millions of years ago — and now you know the math behind it. Up next: City Skyline — composing a scene from reusable building functions.