Skip to content

Magic Juggling

Animation

Construction in p5.js

See the logo study in P5

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:

  1. Main Circle: A large circle that acts as the rotation axis.
  2. Moving Balls: Eight balls rotating around the main circle, following circular trajectories.
  3. 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); } }