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.
Image
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.
Image
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.
Image
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