A physics-based GUI simulation (Java + JavaFX) for calculating and
optimizing arrow trajectories under realistic conditions: gravity,
quadratic aerodynamic drag, and wind.
- Simulates an arrow's flight using 4th-order Runge-Kutta (RK4) numerical integration of the equations of motion (not the simplified no-air-resistance formula most projectile demos use).
- Visualizes the trajectory on an animated canvas, with a target you can place at any distance/height.
- Optimizes:
- Max range: golden-section search finds the launch angle that maximizes range for a given speed (this is not 45° once drag is included — the simulator finds the real optimum).
- Target solving: given a target's distance and height, finds every launch angle at the given speed that lands on it (typically a "flat" low-arc shot and a "lobbed" high-arc shot, when both exist).
src/main/java/com/arrowtrajectory/
├── Main.java Plain launcher (see note below)
├── physics/
│ ├── Vector2D.java Small 2D vector helper
│ ├── ArrowProperties.java Mass, diameter, drag coefficient
│ ├── EnvironmentProperties.java Gravity, air density, wind speed
│ ├── TrajectoryPoint.java One sampled (t, x, y, vx, vy) state
│ ├── SimulationResult.java Full trajectory + summary stats
│ ├── TrajectorySimulator.java RK4 integrator (the physics core)
│ └── OptimizationEngine.java Max-range & target-solving search
└── gui/
├── MainApp.java JavaFX Application, wires everything up
├── ControlPanel.java Left-hand input sliders/fields
├── TrajectoryCanvas.java Draws + animates the trajectory
└── ResultsPanel.java Bottom stats strip
src/test/java/.../physics/ JUnit 5 tests (validated against the
analytic no-drag projectile formula)
src/main/resources/style.css Dark theme stylesheet
Why Main.java exists separately from MainApp: running a JavaFX
Application subclass as the main class of an executable "fat" jar
(built via shade/assembly) without the JavaFX SDK on the module path
can throw Unable to open DISPLAY-adjacent runtime errors on some
setups. A plain launcher class that just calls MainApp.main(args)
avoids that class of problem — this is a well-known JavaFX packaging
workaround, not a bug.
Two forces act on the arrow after release:
-
Gravity — constant, downward:
a_gravity = -g -
Aerodynamic drag — quadratic in relative airspeed (i.e. it accounts for wind):
F_drag = 0.5 * ρ * Cd * A * |v_rel| * v_relwhere
ρis air density,Cdthe drag coefficient,Athe arrow's cross-sectional area, andv_rel = v_arrow - v_wind.
Because drag depends nonlinearly on velocity, the simulator integrates the equations of motion with RK4 at a 1ms step (rather than simple Euler integration), which stays accurate over the arrow's full flight without needing an unreasonably tiny step size.
The max-range search works because range-vs-angle is unimodal (it rises, peaks, then falls) even with drag — so golden-section search reliably converges on the true optimum in a few dozen evaluations.
The target solver doesn't assume the height-vs-angle function is
unimodal; instead it samples angles finely, looks for sign changes in
height_at_target_distance - target_height, and bisects each bracket
it finds. That's why it can report both a low-arc and high-arc solution
for the same target.
Requires JDK 17+ and Maven. JavaFX dependencies are pulled from Maven Central automatically — no manual SDK download needed.
# Run directly (recommended during development)
mvn clean javafx:run
# Or build a runnable fat jar and run it
mvn clean package
java -jar target/arrow-trajectory-simulator-1.0.0.jar
# Run the test suite
mvn test- Adjust Initial Speed, Launch Angle, and Launch Height on the left, plus the arrow's Mass, Shaft Diameter, and Drag Coefficient, and environmental Wind Speed / Air Density.
- Click Simulate & Animate to fly the arrow and see the range, max height, flight time, and impact speed.
- Click Find Max-Range Angle to have the optimizer pick the best angle for the current speed/arrow/environment.
- Check Aim at a target, enter a distance and height, and click Solve Angle(s) For Target to find the angle(s) that hit it.
Default arrow values (26g mass, 6mm shaft, Cd ≈ 1.2) approximate a typical fletched target/hunting arrow. Air density and gravity default to sea-level standard conditions but are both adjustable. This is an educational/portfolio simulation, not a certified ballistics tool.