What is Lucid Geometry?

While pure geometry speaks in the language of perfection, its voice can feel distant. Here, we coax a more organic, lucid flow from its rigid forms. This application is a digital canvas where simple rules blossom into surprising and intricate beauty.

Inspiration

We all had Spirograph as children. I still have mine. It’s magic that rigid gears, wheels, holes, and a pen can create such beautiful shapes. Organic shapes. Spirograph makes the discrete—numbers, lengths, sizes, angles—into something continuous and flowing.

Lucid Geometry is a modern, three-dimensional evolution of that idea. Instead of gears and pens, we use vectors and matrices. Instead of a flat piece of paper, we have an infinite digital space. The core principle remains the same: create complexity and beauty from a simple set of instructions.


Main Controls

  • Add Segment: Adds a new drawing segment to the end of the list.
  • Randomize: Generates a new set of random segments based on the configuration in the “Randomizer Settings” panel.
  • Share: Copies a unique URL to your clipboard that contains your current creation’s configuration.
  • Fullscreen: Toggles a fullscreen view of the 3D canvas.

3D Viewport

  • Rotate: Left-click and drag.
  • Pan: Right-click and drag.
  • Zoom: Scroll with your mouse wheel or trackpad.

Scene Settings

  • Gentle Rotation: Toggles a slow, continuous rotation of the entire scene.
  • Environment: Choose from a variety of High-Dynamic Range Image (HDRI) backgrounds that provide realistic lighting and reflections.
  • Material: Change the surface properties of your geometric shape (e.g., Gold, Silver, Glass).

Segment Editor

Each row represents a single drawing operation. You can add as many segments as you like. The operations are performed in order from top to bottom.

  • Length: The radius of this segment’s circular sweep. Can be positive or negative.
  • Ratio: The speed of rotation for this segment, expressed as a fraction (numerator/denominator). A negative numerator reverses the direction of rotation — this is the key to unlocking a whole family of curves.
  • Axis: The axis (XX, YY, or ZZ) around which to rotate.

Outside vs. Inside Curves

This is directly inspired by how a physical Spirograph works.

Outside curve (Epicycloid): When the numerator is positive, the arm rotates in the same direction as the base rotation. This is equivalent to rolling a small circle around the outside of a larger one — the classic Spirograph motion. The result tends to be looping, outward-reaching shapes.

Inside curve (Hypocycloid): When the numerator is negative, the arm rotates in the opposite direction. This is equivalent to rolling a circle inside a larger one. The result tends to be inward-folding, star-like shapes.

Flower / Rose patterns: When an inside arm’s length is more than half the length of the outer arm, the geometry produces petal-like flower shapes. The number of petals is determined by the speed ratio. For example, an outer arm of length 5 with speed 1 combined with an inner arm of length 4 with speed -6 produces a 6-petal rose.

In Lucid Geometry, you can freely mix outside and inside segments across different axes, creating 3D forms that have no equivalent in the flat world of the original Spirograph.


Technology and Mechanism

The intricate shapes in Lucid Geometry are generated by summing a series of rotating vectors, a concept closely related to the idea of epicycles or a Fourier series. Each “segment” you define acts as an independent rotating “arm,” and their combined movement draws the final shape.

The Core Concept

Imagine a set of arms, all originating from the center of our 3D world. Each arm has a specific length, a rotational speed, and an axis of rotation. They all spin simultaneously and independently around the given XX, YY, or ZZ axis.

The final path is traced by the point that results from adding all the individual arm vectors together, tip to tail. Because the final position is a simple sum, the order of the segments does not matter, making the system flexible and interchangeable.

In the Spirograph analogy, this is like having multiple wheels all centered at the same point, each with its own arm, with the final drawing point being the sum of where each arm is pointing. In Spirograph, the wheels are the segments, the arms are the vectors, and the drawing point is the final position. Lucid Geometry extends this idea into three dimensions and allows for more complex shapes.

Epicycloids and Hypocycloids

The two fundamental families of curves in classical Spirograph have precise mathematical names — and both are fully expressible here.

An epicycloid is traced when a circle rolls around the outside of a fixed circle. In Lucid Geometry, this corresponds to a segment with a positive speed ratio: the arm rotates forward, adding to the sweep of the base motion.

A hypocycloid is traced when a circle rolls around the inside of a fixed circle. This corresponds to a segment with a negative speed ratio: the arm rotates backward, pulling inward. Well-known special cases include the astroid (ratio 1/3-1/3) and the deltoid (ratio 1/2-1/2).

When the inner circle’s radius is more than half the outer circle’s radius, the curve folds back on itself and produces rose or flower patterns. The number of petals kk is determined by the ratio of the two speeds:

k=soutersinnerk = |s_{outer} - s_{inner}|

In Lucid Geometry, these families extend into three dimensions. By assigning inside and outside segments to different axes, you can create forms that are impossible on a flat sheet of paper — helical roses, spiralling stars, and tangled knots that only exist in 3D space.

The Math Behind the Magic

The algorithm generates a smooth curve by calculating the position of a point for thousands of discrete time steps. Each step builds the final position by summing rotating vectors, creating the intricate patterns you see.

Step 1: Simplify Speed Ratios

For each segment ii, the rotation speed is defined by the ratio you provide:

si=numeratoridenominatoris_i = \frac{numerator_i}{denominator_i}

Before calculation, each ratio is reduced to its simplest form using the greatest common divisor (GCD). This ensures the denominators are as small as possible, which directly affects how many cycles the shape needs to complete.

Step 2: Calculate the Period

To create a closed loop, all segments must return to their starting positions simultaneously. This requires finding when all rotations complete whole cycles together.

The algorithm computes the least common multiple (LCM) of all simplified denominators. The total time to trace one complete shape is:

Ttotal=2πLCMT_{total} = 2\pi \cdot LCM

This ensures every segment completes an integer number of rotations, guaranteeing the path closes seamlessly.

Step 3: Complexity Caps

To keep computation tractable, the algorithm enforces two hard limits:

  • Maximum LCM: Capped at 512 cycles
  • Maximum Points: Capped at 20,000 points

If your segment ratios would produce a larger LCM or more points, the algorithm automatically clamps to these limits. You’ll see a warning in the browser console when this happens. The shape will still render, but it may not form a perfectly closed loop.

Step 4: Determine Point Density

The Path Resolution parameter controls how many points are calculated per unit of time. Higher resolution produces smoother curves but requires more computation.

The number of points generated is:

Npoints=min(Ttotalresolution,5000)N_{points} = \min\left(\left\lfloor T_{total} \cdot resolution \right\rfloor, 5000\right)

The time step between consecutive points is then:

Δt=TtotalNpoints\Delta t = \frac{T_{total}}{N_{points}}

Step 5: Generate Each Point

For each time value tt from 00 to TtotalT_{total} (incrementing by Δt\Delta t), the algorithm calculates the position P(t)\vec{P}(t) by summing the contribution of every segment.

For each segment ii:

a) Calculate the rotation angle:

θi(t)=tsi\theta_i(t) = t \cdot s_i

b) Define the base arm vector:

Each segment starts with a vector perpendicular to its rotation axis, with magnitude equal to the segment’s length:

  • If rotating around XX: vbase,i=(0,lengthi,0)\vec{v}_{base,i} = (0, length_i, 0)
  • If rotating around YY: vbase,i=(0,0,lengthi)\vec{v}_{base,i} = (0, 0, length_i)
  • If rotating around ZZ: vbase,i=(lengthi,0,0)\vec{v}_{base,i} = (length_i, 0, 0)

c) Rotate the arm vector:

The base vector is rotated by angle θi(t)\theta_i(t) around its designated axis using a quaternion:

vi(t)=Q(a^i,θi(t))vbase,i\vec{v}_i(t) = Q(\hat{a}_i, \theta_i(t)) \cdot \vec{v}_{base,i}

where Q(a^i,θi)Q(\hat{a}_i, \theta_i) is the quaternion representing rotation by angle θi\theta_i around axis a^i\hat{a}_i.

d) Sum all vectors:

The final position at time tt is the vector sum of all rotated segment vectors:

P(t)=i=1nvi(t)\vec{P}(t) = \sum_{i=1}^{n} \vec{v}_i(t)

This point is added to the path, and the process repeats for the next time step.

A Note on Quaternions vs. Matrices

While rotation can also be represented by matrices, this application uses Quaternions for a crucial reason: to avoid a problem known as Gimbal Lock.

When using a sequence of rotations around standard axes (like Euler angles), it’s possible for two of the axes to align, causing you to lose one degree of rotational freedom. This can lead to unexpected snaps or jerky movements in animations. Quaternions represent rotations in a 4D space and are not susceptible to this issue, resulting in smoother and more predictable rotations, which is essential for the fluid shapes created here. Aren’t you glad you asked?


Three.js Integration

The entire 3D visualization is powered by Three.js, a powerful and intuitive JavaScript library for creating and displaying 3D computer graphics in a web browser.

Here’s how Lucid Geometry uses it:

Scene, Camera, and Renderer: We start by setting up the fundamental components: a Scene to hold all our objects, a PerspectiveCamera to view them, and a WebGLRenderer to draw the scene onto the HTML canvas.

Generating the Geometry: The array of Vector3 points calculated from the segments is used to create a custom path. This path is then fed into TubeGeometry, which generates a smooth, continuous tube shape that follows the path precisely.

Advanced Materials: The application uses MeshPhysicalMaterial, a physically-based rendering (PBR) material. This allows for realistic surfaces with advanced properties like metalness, roughness, and transmission (for glass-like effects).

Environment Lighting: The beautiful and realistic lighting is achieved using High-Dynamic Range Images (HDRIs). We use the RGBELoader to load .hdr files, which wrap the entire scene in a 360-degree image. This image provides both the background and the light sources that reflect off the geometry’s surface.

Interactive Controls: The intuitive camera controls (rotate, pan, zoom) are provided by the OrbitControls utility, which makes navigating the 3D space a breeze.