Using PyGgb (Python and GeoGebra) and AI as visualization tools for mathematic problems
Introduction
Visualization of text based mathematics problems, and the ability to solve problems presented that way, are two well known obstacles in learning mathematics for students anywhere. Not seeing what the text represents hinders the student to, sometimes, even begin analyzing and solving what is at hand.
With GeoGebra we can create visualization tools for better understanding and modeling abstract problems, but this can be a tedious task, both for students and pupils.
Using AI and PyGgb we can quickly create visualization of problems, both directly on the platform, but also (sometimes) in native GeoGebra.
Using Python in GeoGebra to visualize math problems requires a bit of Python knowledge. Sure, you can go in blind, but if the code doesn’t work immediately, it’s easier to troubleshoot if you know a bit of Python.
I will use examples from tests in upper secondary schools, from Nordic and Baltic different countries. I have appended all GeoGebra Python classes at the end, with examples, at the end of this text.
Important notes:
Remember to back up any Python code from geogebra.org/python, since it will disappear when the cache is emptied.
Why?
Visualizing math is an essential and well researched part of solving mathematical problems, when only given a text problem to solve. For students who have a hard time visualizing, either students with aphantasia (which 1 in 20 are estimated to have) or just an untrained visualization skill, using tools to visualize the problems for them can be helpful in solving the problems.
How?
For PyGgb: I have fed a custom chatbot in Google Gemini with this prompt. The chatbot can be found here, but you can give the prompt to an AI of your choice. Then feed the AI with a screenshot or paste of your math problem. We then use geogebra.org/python to paste the code the AI comes up with.
Using Claude, I give it instructions to visualize the problem interactively with JavaScript, directly in the browser. This is more beautiful than GeoGebra, which to some might be an advantage.
Examples
Example 1: Problem 12 from the national test for course Matematik 2b, spring 2015 (link). Past tests from the Swedish national tests in mathematics, both for primary and secondary school forms, can be found here and here.

Asking for a GeoGebra code, I got this from Gemini:
import math
# --- Step 1: Slider for x ---
# x is the length of one side. The interval for x must be (0, 8).
# We choose 0.01 to 7.99 to handle GeoGebra objects.
x_slider = Slider(0.01, 7.99, increment=0.01)
x_slider.value = 4.0 # Starting value at the maximum
x_slider.caption = "Side x (cm)"
# Create a Number object for the other side length (8-x)
side_y_num = Number(8.0 - x_slider.value)
# Create a Number object for the total area A(x) = 2 * x * (8 - x)
Area_num = Number(2.0 * x_slider.value * side_y_num.value)
# --- Step 2: Draw the Rectangles ---
# Dimensions: Width = 8 - x, Height = x (based on the image)
rect_width = side_y_num
rect_height = x_slider
# Base points for the first rectangle
A = Point(-rect_width.value - 0.5, 0, is_visible=False)
B = Point(-0.5, 0, is_visible=False)
C = Point(-0.5, rect_height.value, is_visible=False)
D = Point(-rect_width.value - 0.5, rect_height.value, is_visible=False)
# Base points for the second rectangle (with a small gap)
E = Point(0.5, 0, is_visible=False)
F = Point(rect_width.value + 0.5, 0, is_visible=False)
G = Point(rect_width.value + 0.5, rect_height.value, is_visible=False)
H = Point(0.5, rect_height.value, is_visible=False)
# Rectangle 1
Rectangle1 = Polygon([A, B, C, D], color="#0077ff", opacity=0.3, line_thickness=3)
# Rectangle 2
Rectangle2 = Polygon([E, F, G, H], color="#0077ff", opacity=0.3, line_thickness=3)
# --- Step 3: Dynamic update of rectangles and area ---
# Helper point to show the maximum area value on the graph (Step 4)
# The maximum occurs when x = -b / (2a) = -16 / (2*(-2)) = 4.
# Max area is A(4) = 16*4 - 2*4^2 = 64 - 32 = 32.
Max_Area_Pt = Point(4, 32, size=5, color="#ff0000")
# Point on the graph corresponding to the current value of x
Current_Area_Pt = Point(x_slider.value, Area_num.value, size=5, color="#00aa00")
@x_slider.when_changed
def update_visualization():
# Update the y-side length
side_y_num.value = 8.0 - x_slider.value
# Update the total area
Area_num.value = 2.0 * x_slider.value * side_y_num.value
# Update Rectangle 1 corners
A.x = -side_y_num.value - 0.5
C.y = x_slider.value
D.x = -side_y_num.value - 0.5
D.y = x_slider.value
# Update Rectangle 2 corners
F.x = side_y_num.value + 0.5
G.x = side_y_num.value + 0.5
G.y = x_slider.value
H.y = x_slider.value
# Update the point on the graph
Current_Area_Pt.x = x_slider.value
Current_Area_Pt.y = Area_num.value
# Print current area (shown in the console)
print(f"x = {x_slider.value:.2f} cm")
print(f"Side y (8-x) = {side_y_num.value:.2f} cm")
print(f"Total Area A(x) = {Area_num.value:.2f} cm²")
# --- Step 4: Draw the graph of the area function ---
# Area function: A(x) = -2x² + 16x
Area_Function = Parabola(-2, 16, 0)
Area_Function.color = "#888888"
Area_Function.line_thickness = 2
# Update on startup
update_visualization()
print("Drag the 'Side x (cm)' slider to change the rectangles' dimensions.")
print("Observe how the total area A(x) changes. The graph A(x) = -2x² + 16x is shown.")
print("The red point shows the maximum area A = 32 when x = 4.")
Asking Claude for an interactive page, I got this: https://claude.ai/public/artifacts/bab4905f-d2aa-4f4e-85d5-8cf9cc5c06fa
Example 2: Problem 6 from Part 2 of the national test for course 1T, MAT 1021 spring 2025, upper secondary school, in Norway. Norway’s national exams can be found here.

This is from Gemini, to use in PyGgb:
import math
import time
# --- Constants ---
V = 450 # Volume in cm³
# --- Step 1: Create Slider for Radius (r) ---
# The slider allows the user to adjust the radius and see the effects interactively.
r_slider = Slider(1.0, 10.0, increment=0.1)
r_slider.caption = "Radius, r (cm)"
r_slider.value = 4.15 # Starting value close to the theoretical minimum
# --- Step 2: Create dynamic Numbers for Height (h) and Surface Area (O) ---
# These Number objects will hold the calculated dynamic values.
# IMPORTANT: Number objects DO NOT support the .caption attribute.
h_value = Number(0)
O_value = Number(0)
# Helper point to show the current (r, O(r)) value on the graph
P_O = Point(r_slider.value, O_value.value, size=6, color="#0000FF") # Blue point
# --- Step 3: Calculation Function ---
# This function calculates the height (h) and total surface area (O)
# based on the current radius (r) from the slider.
@r_slider.when_changed
def calculate_cylinder_properties():
r = r_slider.value
# Calculate Height (h)
# h = V / (pi * r^2)
h = V / (math.pi * r**2)
h_value.value = h # Update Number object
# Calculate Total Surface Area (O) - The function we want to minimize
# O(r) = 2*pi*r^2 + 2*pi*r*h = 2*pi*r^2 + 900/r
O = 2 * math.pi * r**2 + 900 / r
O_value.value = O # Update Number object
# Update the position of the point on the graph
P_O.x = r
P_O.y = O
# Print the calculated values to the console for easy reading
print(f"Radius r: {r:.3f} cm")
print(f"Height h: {h:.3f} cm")
print(f"Surface Area O: {O:.3f} cm²")
time.sleep(0) # Crucial to prevent the program from freezing
# Run the calculation with the initial slider value
calculate_cylinder_properties()
# --- Step 4: Visualize the Function O(r) ---
# We plot the function O(r) = 2*pi*r^2 + 900/r by creating many points.
# The graph shows Radius (r) on the x-axis and Surface Area (O) on the y-axis.
def plot_surface_area_function():
# Define the range for the plot
min_r = 1.0
max_r = 10.0
num_points = 100
for i in range(num_points + 1):
# Calculate r for the current point
r_i = min_r + (max_r - min_r) * (i / num_points)
# Calculate O(r_i)
O_i = 2 * math.pi * r_i**2 + 900 / r_i
# Create a point on the graph
# Using size=1 gives the appearance of a continuous curve
Point(r_i, O_i, size=1, color="#FF0000") # Red color for the graph
time.sleep(0) # Prevent freezing during loop execution
# Call the function to draw the graph
plot_surface_area_function()
print("\n--- Interactive Values ---")
print("Use the 'Radius, r (cm)' slider to explore how Height (h) and Surface Area (O) change.")
print("Find the lowest point on the red curve to determine the minimum surface area.")
print(f"Current Height h (cm): {h_value.value:.3f}")
print(f"Current Surface Area O (cm²): {O_value.value:.3f}")
And this is from Claude, an interactive visualization:
https://claude.ai/public/artifacts/fe80f71b-c62a-43a0-ab51-b25722e90075
Example 3: Problem 3 from Matematik FP9 - Folkeskolens prøver Prøven med hjælpemidler, from december 2022. Denmark’s national exams can be found here.

Here is what Gemini produced for me, to use in PyGgb:
import math
import time
# --- Settings and Constants ---
# The constant slope angle
ANGLE_DEGREES = 8.0
# Convert to radians for trigonometric functions
ANGLE_RADIANS = math.radians(ANGLE_DEGREES)
# Visual size for points
PT_SIZE = 5
# --- Task 3.1: Interactive Slope Visualization ---
# Slider to control the horizontal length 'a' (adjacent side)
# The sketch suggests a horizontal length 'a' of ca. 396 m.
a_slider = Slider(100, 1500, increment=1)
a_slider.value = 396
a_slider.caption = "Horizontal Length 'a' (m)"
# Base Points (A and B define the horizontal length 'a')
A = Point(0, 0, size=PT_SIZE, color="#0000FF") # Base, start point (left)
B = Point(a_slider.value, 0, size=PT_SIZE, color="#008800") # Base, end point (right)
# The initial position of the apex C is calculated using h = a * tan(8°)
initial_h = a_slider.value * math.tan(ANGLE_RADIANS)
C = Point(a_slider.value, initial_h, size=PT_SIZE, color="#FF0000") # Apex/Top
# Segments forming the right triangle
seg_a = Segment(A, B, color="#008800", line_thickness=2) # Horizontal length 'a'
seg_h = Segment(B, C, color="#FF0000", line_thickness=2) # Height 'h'
seg_slope = Segment(A, C, color="#0000FF", line_thickness=5) # The actual slope
# Polygon to fill the triangular cross-section
slope_poly = Polygon([A, B, C], color="#AAAAFF", opacity=0.3, line_thickness=0)
# Dynamic Number objects for displaying calculated values (Numbers don't have captions)
h_number = Number(C.y) # Stores Height 'h'
fall_proc_number = Number(0) # Stores Average Grade (h/a) in percent
@a_slider.when_changed
def update_ski_slope():
# 1. Update point B's x-coordinate based on the slider (a)
B.x = a_slider.value
# 2. Calculate and update the height h: h = a * tan(angle)
h_value = a_slider.value * math.tan(ANGLE_RADIANS)
h_number.value = h_value
# 3. Update point C's coordinates
C.x = a_slider.value
C.y = h_value
# 4. Calculate Average Grade (Genomsnittligt Fall): (h/a) * 100
fall_percent = (h_value / a_slider.value) * 100
fall_proc_number.value = fall_percent
# Print current values for context
print(f"--- Slope 1 (Interactive) Updated ---")
print(f"Horizontal Length (a): {a_slider.value:.2f} m")
print(f"Calculated Height (h): {h_value:.2f} m")
print(f"Average Grade (h/a): {fall_percent:.2f} %")
# Initial call to set up the visualization correctly
update_ski_slope()
# --- Task 3.2: Analysis of Slope 2 (Length 1200 m, 8°) ---
# Slope 1 Reference: Use a1 = 396m from the sketch to find h1
a1 = 396
h1 = a1 * math.tan(ANGLE_RADIANS)
# Slope 2: The given length L2 = 1200m is the hypotenuse (pisten's length)
L2 = 1200
# Calculate the height h2: h2 = L2 * sin(angle)
h2 = L2 * math.sin(ANGLE_RADIANS)
# Calculate the ratio h2 / h1 (Should be close to 3)
ratio_h = h2 / h1
# Create a static visualization of Slope 2 (scaled down for the view)
SCALE_X_OFFSET = 500 # Shift the second slope to the right for clear view
# Calculate horizontal length a2: a2 = L2 * cos(angle)
a2 = L2 * math.cos(ANGLE_RADIANS)
A2 = Point(SCALE_X_OFFSET, 0, size=PT_SIZE, color="#00AA00")
B2 = Point(SCALE_X_OFFSET + a2, 0, size=PT_SIZE, color="#00AA00")
C2 = Point(B2.x_number, h2, size=PT_SIZE, color="#FF0000")
seg_a2 = Segment(A2, B2, color="#00AA00", line_thickness=2)
seg_h2 = Segment(B2, C2, color="#FF0000", line_thickness=2)
seg_slope2 = Segment(A2, C2, color="#0000AA", line_thickness=4)
Polygon([A2, B2, C2], color="#FFAAAA", opacity=0.3, line_thickness=0)
# --- Outputting Solutions to Console ---
print(f"\n--- Task 3.2 Solution ---")
print(f"Slope 1 (Horizontal a1=396m): Height h1 = {h1:.2f} m")
print(f"Slope 2 (Hypotenuse L2=1200m): Height h2 = {h2:.2f} m")
print(f"Height ratio h2/h1 = {ratio_h:.2f}")
print(f"Since the ratio is approximately 3.01, Sofia's observation that h2 is 3 times h1 is correct (based on proportional triangles).")
# --- Task 3.3: Average Grade Calculation ---
# The formula is: Average Grade = h/a * 100%. In a right triangle, h/a = tan(angle).
tan_8_percent = math.tan(ANGLE_RADIANS) * 100
print(f"\n--- Task 3.3 Solution ---")
print(f"Average Grade = h/a = tan(8°) * 100")
print(f"Calculated Average Grade: {tan_8_percent:.2f} %")
print(f"The mother claims the slope corresponds to 15% to 25%. Since {tan_8_percent:.2f}% is within the range 15% to 25%, Sofia's mother is **correct**.")
And this visualization is what Claude produced:
https://claude.ai/public/artifacts/4932dc52-992e-4be5-ad40-264cc77bad90