Visualizing Golf Flight Trajectories with React and Three.js

Golf has always been a game of precision and physics. One of the most fascinating aspects of the sport is how the club path and face angle influence the trajectory of the ball. It’s something I have always largely misunderstood. As someone captivated by these dynamics, as well as a burning passion to understand why I still pull my golf shot 20 yards right into what we refer to as a “banana slice”, I set out to build a Golf Flight Visualizer, a React-based application that simulates golf shots in 3D using React Three Fiber.

This project, hosted at golf-flight.com, lets users tweak swing parameters and watch the ball flight unfold in real time. It was a project that originated from my fascination in golf simulators, as well as a way to learn and understand swing path versus face angle. It also lets me appreciate and accept that my 8 degree outside in swing path, can in fact work, but no, it is not a good idea to open or close my face to the extremes to try and hit that draw I’ve always dreamed of.

That said, it’s a work in progress—some mobile layout issues need fixing, and a few bugs still sneak their way into the experience, but it’s otherwise a fun project to both learn new technology as well as learn feedback mechanics for my swing.

We call this the “power” fade. Not to be confused with a golfer that has no idea what they are doing.

Understanding the Physics Behind Golf Shots

Club Path and Face Angle

To better understand the basics of a golf swing and why the ball moves, two primary factors influence a golf ball’s trajectory:

  • Club Face Angle: Determines the initial direction of the shot.
  • Club Path Angle: Influences spin and curvature.

The overall difference in these values creates a shot with more spin, which therefore creates more drastic dispersion for you as a golfer to control.

By comparing these two values, we can categorize shots into common golf shot types:

  • Push / Pull: The ball starts right or left based on face angle.
  • Draw / Fade: The ball slightly curves left or right due to spin differences.
  • Hook / Slice: Excessive spin can create extreme curvature.
I lean anywhere from slice, to fade, to pull, to pull draw, as well as topping the ball 5 yards, but am making some improvements with my new found understanding of this concept.

Contact is not programmed in this module thankfully, so everything is dead center of the club face. In my dreams right?

Magnus Effect and Side Spin

While I thought this endeavor would be a simple program that would be something along the lines of x, y, and z coordinates, I slowly found myself talking to ChatGPT more about physics then golf and computers. I completely underestimated the complexity of how a ball spins through the air, and better yet, why it spins through the air.

This eventually led to my discovery of the Magnus effect.

The Magnus effect is a fascinating principle that plays a huge role in shot curvature. When a spinning ball moves through the air, it creates a pressure difference that forces it to curve. This is due to the way air moves faster on one side of the ball and slower on the other, creating lift and deviation. It’s a similar concept to aviation, where the rotational force around a wing “lifts” the plane.

In golf, spin is calculated based on the difference between club path and face angle. The greater the discrepancy, the more side spin is generated, resulting in a greater curve in flight. The side spin, combined with the Magnus force, alters the ball’s movement frame-by-frame, dynamically influencing its trajectory as it travels towards the target.

To model this effect in the simulation, we apply a cross-product calculation to determine the Magnus force acting on the ball at each time step. This force is then factored into the velocity components, making the ball curve naturally based on physics rather than pre-set animations. The result? A shot that behaves just like it would on a real golf course—hooks, slices, draws, fades, or that perfectly straight shot we all chase.


How the Golf Flight Visualizer Works

The application consists of a 3D golf course environment with a tee, ball, and a distant pin. Users can input:

  • Club Face Angle (°)
  • Club Path Angle (°)
  • Swing Speed (mph)
  • Launch Angle (°)
  • Pin Distance (yds)

Pressing Hit Shot calculates the ball’s trajectory and renders a real-time flight path in Three.js.

It took me more time than I care to admit to get this canvas look somewhat presentable.

Trajectory Calculation

The simulation follows these steps:

  • Convert swing speed to initial velocity components.
// initial velocity
let vX = ballSpeedMs * Math.cos(launchRad) * Math.sin(faceRad);
let vY = ballSpeedMs * Math.sin(launchRad);
let vZ = ballSpeedMs * Math.cos(launchRad) * Math.cos(faceRad);
  • Compute side spin RPM based on face-to-path difference.
const degDiff = input.clubPath - input.clubFace;
const side = degDiff * swingSpeed * 10;
setSideSpinRpm(side);
  • Apply gravity and Magnus effect at each time step.
// dt -> alters ball speed in visual
const dt = 0.002;
const gravity = -9.81;
const maxTime = 8;
let magnusConst = 0.0003;

let posX = 0, posY = 0.1, posZ = 0;
const points = [];

for (let t = 0; t < maxTime; t += dt) {
  posX += vX * dt;
  posY += vY * dt;
  posZ += vZ * dt;

  vY += gravity * dt;

  // magnus
  const [mx, my, mz] = cross(spinAxis[0], spinAxis[1], spinAxis[2], vX, vY, vZ);
  vX += magnusConst * mx * dt;
  vY += magnusConst * my * dt;
  vZ += magnusConst * mz * dt;
  • Stop simulation when the ball touches the ground, calculating carry distance.
  if (posY <= 0) {
    posY = 0;
    const carry = Math.sqrt(posX*posX + posZ*posZ) / 0.9144;
    setCarryDistance(carry);
    break;
  }
  points.push([posX, posY, posZ]);
}
setTrajectory(points);
setShotTaken(true);

Key Features of the Implementation

1. React Three Fiber for 3D Rendering

The application uses @react-three/fiber for rendering the 3D environment. Three.js elements, such as the tee, fairway, green, and pin, provide a realistic feel.

2. Dynamic Ball Trajectory Simulation

Each shot is visualized with a real-time moving golf ball and a red tracer path that follows the ball’s movement. The ball’s position updates frame-by-frame using React’s useFrame() hook.

3. Realistic Physics with Magnus Effect

To introduce realistic ball flight curvature, we calculate Magnus force using a cross-product function:

function cross(ax, ay, az, bx, by, bz) {
  return [
    ay * bz - az * by,
    az * bx - ax * bz,
    ax * by - ay * bx,
  ];
}

This force is applied to alter the ball’s velocity vectors over time.

4. Shot Categorization Algorithm

Each shot is classified based on face-to-path relationship:

function getShotCategory(faceDeg, pathDeg, sideSpinRpm) {
  // Set bigger thresholds to reduce “false positives.”
  const faceThreshold = 0.5; 
  const diffThreshold = 0.5;  
  const bigSpin = 2400;  

  // 1) Start direction
  let startDir = "Straight";
  if (faceDeg > faceThreshold) {
    startDir = "Push";  // ball starts right if face > +1
  } else if (faceDeg < -faceThreshold) {
    startDir = "Pull";  // ball starts left if face < -1
  }

  // 2) Curvature
  let curve = "Straight";
  const diff = pathDeg - faceDeg;
  // e.g. if diff>+2 => left curve => “Draw” or “Hook”
  // if diff<-2 => right curve => “Fade” or “Slice”
  if (diff > diffThreshold) {
    // left curve
    if (Math.abs(sideSpinRpm) > bigSpin) curve = "Hook";
    else curve = "Draw";
  } else if (diff < -diffThreshold) {
    // right curve
    if (Math.abs(sideSpinRpm) > bigSpin) curve = "Slice";
    else curve = "Fade";
  }

This allows golfers to instantly recognize if their shot is a Push-Fade, Pull-Draw, or a Straight Drive, which then displays on the top right.

As of right now, these values are arbitrary. The spin rate threshold being 2400 RPM has no tie to actual real-life physics, but was merely a threshold that got the values that made somewhat logical sense.

5. Interactive UI for Input Controls

A simple UI allows users to modify shot parameters in real time. React state updates trigger recalculations, ensuring each input change dynamically affects shot results.

This is also an area of improvement beyond the initial proof-of-concept, of getting a UI that is dynamic to the user’s web session resolution, as well as allowing minimization. It’s also worth noting that getting negative values on mobile seems to not work correctly, as React seems to eliminate the up/down slider on these values.


What’s Next?

This project opens doors for further enhancements:

  • Fixing mobile layout issues: Some UI elements need better scaling for small screens. Additionally, the negative numbers in mobile outright do not work.
  • Better physics: I am not satisfied with the amount of distance coming off hooks and slices.
  • Wind Effects: Introduce variable wind speeds and directions for some variety and making the app fresh and fun.
  • Better fade/draw detection: The algorithm is slightly off and classifications may not seem perfect.
  • Turf Interaction: Simulate bounce and roll upon landing so you can hit low piercers and high spinners.
  • Custom Club Selection: Factor in different clubs with variable launch conditions.
  • Player Profiles: Store user swing tendencies over time, as well as saved club presets.
  • Target Line: Add a straight target line to the pin so the relationship makes more intuitive sense.

Conclusion

The Golf Flight Visualizer is a fun and educational tool that merges my passion for golf and software development. By using React, Three.js, and real-world physics, this project offers an engaging way to understand how club path and face angles shape shot outcomes. Whether you’re a golfer looking to improve or a developer interested in physics simulations, this project is a great way to explore the mechanics of ball flight, as well as provide instance feedback for your golf swing!

If you’re interested in trying it out or contributing, feel free to reach out! The GitHub repository can be viewed here: https://github.com/timothyedwarddean/golf-flight


What’s Your Typical Shot Shape?

Are you more of a Draw or Fade player? Or are you like me and enjoy torturing yourself by slicing one 40 yards into the woods? Let me know in the comments!


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *