Magic Juggling
Animation
Construction in p5.js
Animation Study in GeoGebra
Magic Juggling: A Moving Optical Illusion
Introduction
Magic Juggling is an animation that plays with the viewer’s visual perception. At first glance, it seems like a series of balls are moving in straight lines across the screen. However, upon closer inspection, it becomes clear that these balls are actually following a rotating circular path. This effect creates a fascinating optical illusion, where the apparent rectilinear motion is combined with an underlying circular pattern.
Study in GeoGebra
Before implementing the animation in code, I conducted a study in GeoGebra to analyze the trajectories and understand how this optical illusion is produced. In GeoGebra, I explored:
- The relationship between the radius of the main circle and the radius of the balls' trajectories.
- How the rotation of the circle affects the position of the balls.
- The superposition of straight lines and circles to create the visual effect.
This analysis allowed me to transfer the geometric principles into an implementation in p5.js, where I developed the interactive animation.
Animation Concept
The animation is based on the following key elements:
- Main Circle: A large circle that acts as the rotation axis.
- Moving Balls: Eight balls rotating around the main circle, following circular trajectories.
- Optical Illusion: Although the balls move in circles, their motion combined with the rotation of the main circle creates the illusion that they are moving in straight lines.
Code (p5.js)
```javascript let Radio = 300; let Radio2 = Radio - 30; let RadioC = 13; let Radio3 = Radio2 + RadioC;
let opacidad = 255; let button, button2; let mostrar = false; let mostrar2 = true; let paso = 0;
function mostrarlinea() { mostrar = !mostrar; }
function mostrarlinea2() { mostrar2 = !mostrar2; }
function setup() { createCanvas(2 * Radio + 100, 2 * Radio + 100); button = createButton('Show Lines'); button2 = createButton('Show Circle'); button2.position(130, 2 * Radio + 100 - 40); button.position(20, 2 * Radio + 100 - 40);
button.mousePressed(mostrarlinea); button2.mousePressed(mostrarlinea2); }
function draw() { background(0); fill(255, 0, 0); ellipse(Radio, Radio, 2 * Radio3, 2 * Radio3); fill(0);
if (mostrar) { for (let t = 0; t < 1; t += 0.01) { strokeWeight(t); line(0, 0, 2 * Radio, 2 * Radio); line(Radio - Radio * tan(PI / 8), 0, Radio + Radio * tan(PI / 8), 2 * Radio); line(Radio, 0, Radio, 2 * Radio); line(Radio + Radio * tan(PI / 8), 0, Radio - Radio * tan(PI / 8), 2 * Radio); line(2 * Radio, 0, 0, 2 * Radio); line(0, Radio - Radio * tan(PI / 8), 2 * Radio, Radio + Radio * tan(PI / 8)); line(0, Radio, 2 * Radio, Radio); line(0, Radio + Radio * tan(PI / 8), 2 * Radio, Radio - Radio * tan(PI / 8)); } }
strokeWeight(1); let xdibujo = Radio + (Radio2 / 2) * cos(paso); let ydibujo = Radio + (Radio2 / 2) * sin(paso);
paso += PI / 180;
if (mostrar2) { noFill(); ellipse(xdibujo, ydibujo, Radio2, Radio2); }
fill(255); for (let i = 0; i < 8; i++) { let xdibujo2 = xdibujo + (Radio2 / 2) * cos(i * PI / 4 - paso); let ydibujo2 = ydibujo + (Radio2 / 2) * sin(i * PI / 4 - paso); ellipse(xdibujo2, ydibujo2, 2 * RadioC, 2 * RadioC); } }