### Basic Projectile Setup Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Initialize a projectile with FPS, starting position, initial velocity, and acceleration (e.g., gravity). This simulates motion under constant force. ```go import ( "github.com/charmbracelet/harmonica" ) // Launch a projectile from (0, 0) with initial velocity and gravity proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, // Starting position harmonica.Vector{X: 10, Y: 20, Z: 0}, // Initial velocity harmonica.Gravity, // Acceleration ) // Update each frame func update() { pos := proj.Update() render(pos) } ``` -------------------------------- ### Basic Spring Setup Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Initialize a reusable spring with specified FPS, angular frequency, and damping ratio. This setup is ideal for animating scalar values like position. ```go import ( "github.com/charmbracelet/harmonica" ) // Create a spring once at initialization spring := harmonica.NewSpring( harmonica.FPS(60), // Framerate: 60 FPS 6.0, // Angular frequency: controls speed 0.5, // Damping ratio: controls springiness ) // Reuse the same spring across multiple values var ( x, xVel float64 = 0, 0 y, yVel float64 = 0, 0 z, zVel float64 = 0, 0 ) targetX, targetY, targetZ := 100.0, 50.0, 25.0 ``` -------------------------------- ### Point Usage Example Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/types.md Demonstrates how to create and access components of a Point. Ensure the harmonica package is imported. ```go import "github.com/charmbracelet/harmonica" // Create a point at origin origin := harmonica.Point{X: 0, Y: 0, Z: 0} // Create a point at (10, 20, 5) pos := harmonica.Point{X: 10, Y: 20, Z: 5} // Access components fmt.Printf("X: %f\n", pos.X) ``` -------------------------------- ### Spring Animation Example Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/README.md Demonstrates how to create and use a spring animation. The spring coefficients are computed once, and the `Update` method is called each frame to get the new position and velocity. ```go import "github.com/charmbracelet/harmonica" // Create once spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) // Use every frame var x, xVel = 0.0, 0.0 targetX := 100.0 for { x, xVel = spring.Update(x, xVel, targetX) // Render at position x } ``` -------------------------------- ### Full Example: Spring Animation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/index.md This example demonstrates animating a sprite's X position towards a target value over 300 frames using the Spring animation primitive. ```go package main import ( "fmt" "github.com/charmbracelet/harmonica" ) func main() { spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) var x, xVel = 0.0, 0.0 targetX := 100.0 // Animate 300 frames for i := 0; i < 300; i++ { x, xVel = spring.Update(x, xVel, targetX) fmt.Printf("Frame %d: x = %.2f\n", i, x) } } ``` -------------------------------- ### Example: Falling Object Simulation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Set up a projectile simulation for an object falling from a height with no initial velocity, using standard gravity. ```go proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 100, Z: 0}, harmonica.Vector{X: 0, Y: 0, Z: 0}, harmonica.Gravity, ) ``` -------------------------------- ### Example: Launched Projectile Simulation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Configure a projectile simulation for an object launched with an initial velocity at a specific angle. ```go proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, harmonica.Vector{X: 20, Y: 30, Z: 0}, harmonica.Gravity, ) ``` -------------------------------- ### Full Example: Projectile Motion Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/index.md This example simulates a ballistic projectile launched at a 45-degree angle, calculating its trajectory until it hits the ground (Y < 0). ```go package main import ( "fmt" "math" "github.com/charmbracelet/harmonica" ) func main() { speed := 30.0 angle := 45.0 * math.Pi / 180.0 proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, harmonica.Vector{ X: speed * math.Cos(angle), Y: speed * math.Sin(angle), Z: 0, }, harmonica.Gravity, ) for { pos := proj.Update() if pos.Y < 0 { fmt.Printf("Final range: %.2f m\n", pos.X) break } } } ``` -------------------------------- ### Projectile Motion Example Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/README.md Shows how to initialize and update a projectile motion simulation. The `NewProjectile` function sets up the initial state, and `Update` is called each frame to advance the projectile's position. ```go import "github.com/charmbracelet/harmonica" // Create proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, harmonica.Vector{X: 10, Y: 20, Z: 0}, harmonica.Gravity, ) // Use every frame for { pos := proj.Update() // Render at position if pos.Y < 0 { break // Hit ground } } ``` -------------------------------- ### Create a Projectile Motion Instance Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Initialize a projectile motion simulation with framerate, starting position, initial velocity, and acceleration (e.g., gravity). This is typically done once per projectile. ```go proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, harmonica.Vector{X: 10, Y: 20, Z: 0}, harmonica.Gravity, ) ``` -------------------------------- ### NewProjectile Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/exported-symbols.md Creates and initializes a new Projectile simulator. This function sets up the initial conditions for simulating projectile motion, including its starting position, velocity, and acceleration. ```APIDOC ## NewProjectile ### Description Create a projectile simulator. ### Signature `func NewProjectile(deltaTime float64, initialPosition Point, initialVelocity, initalAcceleration Vector) *Projectile` ### Parameters * `deltaTime` (float64) - The time step for the simulation. * `initialPosition` (Point) - The starting position of the projectile. * `initialVelocity` (Vector) - The initial velocity of the projectile. * `initalAcceleration` (Vector) - The initial acceleration of the projectile. ### Returns * `*Projectile` - A pointer to the initialized Projectile simulator. ``` -------------------------------- ### Vector Usage Example Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/types.md Demonstrates creating Vector instances for velocity and acceleration, including using the predefined Gravity constant. Ensure the harmonica package is imported. ```go import "github.com/charmbracelet/harmonica" // Velocity: moving right and up vel := harmonica.Vector{X: 5, Y: 10, Z: 0} // Acceleration: gravity (downward) acc := harmonica.Gravity // {0, -9.81, 0} // Custom acceleration customAcc := harmonica.Vector{X: 1, Y: -9.81, Z: 0} ``` -------------------------------- ### Example: Projectile in Y-Down System Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Simulate projectile motion in a coordinate system where the Y-axis points downwards, using `TerminalGravity`. ```go proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 100, Y: 0, Z: 0}, harmonica.Vector{X: 0, Y: 10, Z: 0}, harmonica.TerminalGravity, ) ``` -------------------------------- ### Smooth Movement with Spring Animation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Use a pre-configured spring animation to smoothly move values towards their targets. This example shows movement for X and Y coordinates. ```go x, xVel = spring.Update(x, xVel, targetX) y, yVel = spring.Update(y, yVel, targetY) ``` -------------------------------- ### Configuring Springs for Fixed Framerates Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Explains the importance of matching the `FPS()` parameter in `harmonica.NewSpring` to the application's actual framerate for correct animation speeds. Includes examples for 60 FPS, 30 FPS, and variable framerates. ```go // For 60 FPS application spring60 := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) // For 30 FPS application spring30 := harmonica.NewSpring(harmonica.FPS(30), 6.0, 0.5) // For variable framerate (using measured delta time) deltaTime := measuredDeltaTime // From game engine spring := harmonica.NewSpring(deltaTime, 6.0, 0.5) ``` -------------------------------- ### Spring Immutable Reuse Example Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/architecture.md Demonstrates how to create a Spring object once and reuse it for multiple animations. This leverages the immutable nature of the Spring type for safe concurrent use. ```go var spring = NewSpring(FPS(60), 6.0, 0.5) // Create once func Update() { x, xVel = spring.Update(x, xVel, targetX) // Use many times y, yVel = spring.Update(y, yVel, targetY) z, zVel = spring.Update(z, zVel, targetZ) } ``` -------------------------------- ### Create a New Projectile Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/projectile.md Initializes a new projectile with specified time step, starting position, initial velocity, and constant acceleration. Use FPS(n) for time step and constants like Gravity for acceleration. ```go import "github.com/charmbracelet/harmonica" // Create a projectile launched at (0, 0, 0) with velocity upward and gravity applied proj := harmonica.NewProjectile( harmonica.FPS(60), // 60 FPS harmonica.Point{X: 0, Y: 0, Z: 0}, // Starting position harmonica.Vector{X: 10, Y: 20, Z: 0}, // Initial velocity harmonica.Gravity, // Acceleration (gravity) ) ``` -------------------------------- ### Guided Projectile with Spring Control Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Demonstrates how to use a Spring to provide mid-flight course corrections for a Projectile. Note that direct velocity modification of the Projectile is pseudo-code as the library may not expose such a method. ```go type GuidedProjectile struct { projectile *harmonica.Projectile spring harmonica.Spring targetX float64 } func (gp *GuidedProjectile) Update() harmonica.Point { pos := gp.projectile.Update() // Spring-based guidance (slight course correction) vel := gp.projectile.Velocity() newVX, velVX := gp.spring.Update(vel.X, 0, gp.targetX) // Manually update velocity with correction // (Note: Projectile doesn't have a method for this, so this is pseudo-code) return pos } ``` -------------------------------- ### Smooth Color Transition Animation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/examples.md Smoothly animate color transitions for UI widgets. This example uses Harmonica springs to interpolate RGB color values, allowing for fluid visual changes. ```go import "image/color" type ColorFloat struct { R, G, B float64 } type Widget struct { color ColorFloat colorVel ColorFloat spring harmonica.Spring } func NewWidget() *Widget { return &Widget{ color: ColorFloat{R: 255, G: 255, B: 255}, spring: harmonica.NewSpring(harmonica.FPS(60), 4.0, 0.5), } } func (w *Widget) TransitionTo(r, g, b float64) { // Target color is already set, Update() will smooth toward it } func (w *Widget) Update(targetR, targetG, targetB float64) { w.color.R, w.colorVel.R = w.spring.Update(w.color.R, w.colorVel.R, targetR) w.color.G, w.colorVel.G = w.spring.Update(w.color.G, w.colorVel.G, targetG) w.color.B, w.colorVel.B = w.spring.Update(w.color.B, w.colorVel.B, targetB) } func (w *Widget) RenderColor() color.RGBA { return color.RGBA{ R: uint8(w.color.R), G: uint8(w.color.G), B: uint8(w.color.B), A: 255, } } ``` -------------------------------- ### Simulate Projectile Firing Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/examples.md Simulate projectile motion with gravity using Harmonica's Projectile type. This example shows how to fire projectiles and update their positions over time, removing them when off-screen. ```go import ( "math" "github.com/charmbracelet/harmonica" ) type Cannon struct { projectiles []*harmonica.Projectile } func (c *Cannon) Fire(x, y, angle, speed float64) { rad := angle * math.Pi / 180.0 proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: x, Y: y, Z: 0}, harmonica.Vector{ X: speed * math.Cos(rad), Y: speed * math.Sin(rad), Z: 0, }, harmonica.Gravity, ) c.projectiles = append(c.projectiles, proj) } func (c *Cannon) UpdateProjectiles() []harmonica.Point { var positions []harmonica.Point var alive []*harmonica.Projectile for _, proj := range c.projectiles { pos := proj.Update() // Remove if off-screen if pos.Y < -100 { continue } alive = append(alive, proj) positions = append(positions, pos) } c.projectiles = alive return positions } ``` -------------------------------- ### Projectile Mutable State Example Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/architecture.md Illustrates how to use the Projectile type, which encapsulates mutable state. State is updated via the Update() method, and accessed immutably through accessor methods. ```go proj := NewProjectile(...) // Mutation through Update() pos := proj.Update() // Immutable access through accessors vel := proj.Velocity() acc := proj.Acceleration() ``` -------------------------------- ### Stagger Animation Starts Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/examples.md Delay the start of animations sequentially using a frame counter and a delay value. This is useful for creating staggered visual effects. ```go type StaggeredAnimation struct { spring harmonica.Spring animations []*Animation completedCount int } type Animation struct { delay int frame int value float64 vel float64 target float64 } func (s *StaggeredAnimation) Update() { for _, anim := range s.animations { anim.frame++ // Wait for delay before starting if anim.frame < anim.delay { continue } // Animate anim.value, anim.vel = s.spring.Update( anim.value, anim.vel, anim.target, ) // Check if settled if math.Abs(anim.target-anim.value) < 0.01 { s.completedCount++ } } } ``` -------------------------------- ### Creating Point Instances Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/coordinates.md Demonstrates how to create Point instances, including the origin, using named fields, and uninitialized zero values. ```go import "github.com/charmbracelet/harmonica" // Origin origin := harmonica.Point{X: 0, Y: 0, Z: 0} // Using named fields pos := harmonica.Point{X: 10.5, Y: 20.3, Z: 5.1} // Uninitialized (zero values) var zeroPoint harmonica.Point // {0, 0, 0} ``` -------------------------------- ### Creating Vector Instances Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/coordinates.md Demonstrates how to create Vector instances, including the zero vector, velocity, and gravity vectors. ```go import "github.com/charmbracelet/harmonica" // Zero vector zeroVec := harmonica.Vector{X: 0, Y: 0, Z: 0} // Velocity vector velocity := harmonica.Vector{X: 10, Y: 20, Z: 5} // Acceleration (gravity) gravity := harmonica.Gravity // {0, -9.81, 0} ``` -------------------------------- ### Get Projectile Acceleration Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/projectile.md Retrieves the constant acceleration vector applied to the projectile, typically representing gravity. This value does not change during simulation. ```go acc := proj.Acceleration() fmt.Printf("Acceleration: (%.2f, %.2f, %.2f)\n", acc.X, acc.Y, acc.Z) ``` -------------------------------- ### Projectile.Position Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/exported-symbols.md Retrieves the current position of the projectile in 3D space. This method allows you to get the projectile's location at any point during the simulation. ```APIDOC ## Projectile.Position ### Description Get current position. ### Signature `func (p *Projectile) Position() Point` ### Parameters * `p` (*Projectile) - A pointer to the Projectile instance. ### Returns * `Point` - The current 3D position of the projectile. ``` -------------------------------- ### Initialize a New Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/spring.md Creates a new Spring instance with specified time step, angular frequency, and damping ratio. Use harmonica.FPS() for convenient time step calculation. Ensure angularFrequency and dampingRatio are non-negative. ```go import "github.com/charmbracelet/harmonica" // Create a 60 FPS spring with moderate speed and slight springiness spring := harmonica.NewSpring( harmonica.FPS(60), // 60 frames per second 6.0, // angular frequency (speed) 0.5, // damping ratio (springiness) ) ``` -------------------------------- ### Responsive Menu Animation with Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Demonstrates using a Spring to animate menu item positions. The target X position is determined by the menu's expanded state, allowing for smooth transitions. ```go type MenuItem struct { x, xVel float64 } type Menu struct { items []*MenuItem spring harmonica.Spring expanded bool } func (m *Menu) Update() { targetX := 0.0 if m.expanded { targetX = 200.0 } for _, item := range m.items { item.x, item.xVel = m.spring.Update(item.x, item.xVel, targetX) } } ``` -------------------------------- ### Spring Reuse for Performance Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Illustrates the recommended practice of creating `harmonica.Spring` objects once at initialization and reusing them, rather than creating new ones every frame. ```go // GOOD: Create once var spring = harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) func Update() { x, xVel = spring.Update(x, xVel, targetX) y, yVel = spring.Update(y, yVel, targetY) // ... } ``` ```go // AVOID: Creating every frame (wasteful) func Update() { spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) x, xVel = spring.Update(x, xVel, targetX) // ... } ``` -------------------------------- ### Particle System Implementation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Shows how to implement a particle system using `harmonica.Projectile` for managing particle movement and lifetime. ```go import ( "github.com/charmbracelet/harmonica" ) type Particle struct { projectile *harmonica.Projectile lifetime int maxLife int } type ParticleSystem struct { particles []*Particle } func (ps *ParticleSystem) Emit(x, y, vx, vy float64) { p := &Particle{ projectile: harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: x, Y: y, Z: 0}, harmonica.Vector{X: vx, Y: vy, Z: 0}, harmonica.Gravity, ), lifetime: 0, maxLife: 120, // 2 seconds at 60 FPS } ps.particles = append(ps.particles, p) } func (ps *ParticleSystem) Update() { // Update all alive particles for i := len(ps.particles) - 1; i >= 0; i-- { p := ps.particles[i] p.lifetime++ pos := p.projectile.Update() // Remove if past lifetime if p.lifetime > p.maxLife { ps.particles = append(ps.particles[:i], ps.particles[i+1:]...) continue } // Render particle renderParticle(pos, p.lifetime, p.maxLife) } } ``` -------------------------------- ### Get Current Projectile Position Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/projectile.md Retrieves the projectile's current position (X, Y, Z coordinates) without advancing its motion. Useful for rendering or checking boundaries. ```go pos := proj.Position() fmt.Printf("Current position: (%.1f, %.1f, %.1f)\n", pos.X, pos.Y, pos.Z) ``` -------------------------------- ### Spring Initialization Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/architecture.md Shows the function signature for creating a new Spring object. The initialization process involves validating inputs, detecting the damping regime, and computing specific coefficients. ```go spring := NewSpring(deltaTime, angularFrequency, dampingRatio) ``` -------------------------------- ### Using Points in Projectile Simulations Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/coordinates.md Shows how Point types are used to define the initial position and track the position of a projectile over time. ```go // Initial position in NewProjectile initialPos := harmonica.Point{X: 100, Y: 0, Z: 0} // Returned by Projectile.Update() pos := proj.Update() fmt.Printf("Position: (%.2f, %.2f, %.2f)\n", pos.X, pos.Y, pos.Z) // Returned by Projectile.Position() currentPos := proj.Position() ``` -------------------------------- ### Using Vectors in Projectile Simulations Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/coordinates.md Illustrates how Vector types are used for initial velocity and acceleration, and how to retrieve current velocity and acceleration. ```go // Initial velocity in NewProjectile initialVel := harmonica.Vector{X: 15, Y: 25, Z: 0} // Acceleration (often gravity) acc := harmonica.TerminalGravity // Returned by Projectile.Velocity() vel := proj.Velocity() fmt.Printf("Velocity: (%.2f, %.2f, %.2f)\n", vel.X, vel.Y, vel.Z) // Returned by Projectile.Acceleration() acc := proj.Acceleration() ``` -------------------------------- ### Get Current Projectile Velocity Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/projectile.md Retrieves the projectile's current velocity vector (X, Y, Z components) without updating its motion. This allows for calculating current speed or other velocity-dependent properties. ```go import "math" vel := proj.Velocity() speed := math.Sqrt(vel.X*vel.X + vel.Y*vel.Y + vel.Z*vel.Z) fmt.Printf("Current speed: %.2f units/sec\n", speed) ``` -------------------------------- ### Spring-Based Camera Follow Implementation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Provides a `Camera` struct that uses a `harmonica.Spring` to smoothly follow a target position, creating a natural camera movement. ```go type Camera struct { x, xVel float64 y, yVel float64 spring harmonica.Spring } func (c *Camera) Follow(targetX, targetY float64) { c.x, c.xVel = c.spring.Update(c.x, c.xVel, targetX) c.y, c.yVel = c.spring.Update(c.y, c.yVel, targetY) } ``` -------------------------------- ### Creating Springs with Different Easing Effects Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Demonstrates how to configure `harmonica.Spring` with different damping ratios to achieve 'easing in' (underdamped), 'easing out' (overdamped), and linear motion. ```go // Easing in (underdamped, fast start) easeIn := harmonica.NewSpring(harmonica.FPS(60), 8.0, 0.3) // Easing out (overdamped, slow end) easeOut := harmonica.NewSpring(harmonica.FPS(60), 5.0, 1.5) // Linear (critically damped) linear := harmonica.NewSpring(harmonica.FPS(60), 6.0, 1.0) ``` -------------------------------- ### Initialize Projectile Motion Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Creates a new Projectile instance with specified FPS, initial position, velocity, and gravity. The Update method advances the projectile's motion based on physics. ```go proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, harmonica.Vector{X: 10, Y: 20, Z: 0}, harmonica.Gravity, ) // Physics take over; motion is inevitable pos := proj.Update() ``` -------------------------------- ### Create a Spring Animation Instance Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Create a new spring animation instance with specified framerate, angular frequency, and damping ratio. This is typically done once. ```go spring := harmonica.NewSpring( harmonica.FPS(60), 6.0, 0.5, ) ``` -------------------------------- ### Update Projectile Motion and Get New Position Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/projectile.md Advances the projectile's motion by one time step, updating its position and velocity based on acceleration. This function should be called once per frame in an animation or game loop. It returns the new position. ```go import ( "fmt" "github.com/charmbracelet/harmonica" ) proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, harmonica.Vector{X: 10, Y: 20, Z: 0}, harmonica.Gravity, ) // In your animation/game loop: for { pos := proj.Update() fmt.Printf("Position: X=%.2f, Y=%.2f, Z=%.2f\n", pos.X, pos.Y, pos.Z) // Stop when projectile falls below ground if pos.Y < 0 { break } } ``` -------------------------------- ### Initialize and Animate a Spring Source: https://github.com/charmbracelet/harmonica/blob/master/README.md Use `NewSpring` to initialize a spring with framerate, angular frequency, and damping. Call `Update` on each frame to animate values towards their targets. ```go import "github.com/charmbracelet/harmonica" // A thing we want to animate. sprite := struct{ x, xVelocity float64 y, yVelocity float64 }{} // Where we want to animate it. const targetX = 50.0 const targetY = 100.0 // Initialize a spring with framerate, angular frequency, and damping values. spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) // Animate! for { sprite.x, sprite.xVelocity = spring.Update(sprite.x, sprite.xVelocity, targetX) sprite.y, sprite.yVelocity = spring.Update(sprite.y, sprite.yVelocity, targetY) time.Sleep(time.Second/60) } ``` -------------------------------- ### Create and Update Projectile Motion Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/index.md Initialize a Projectile with FPS, initial position, velocity, and acceleration. Call Update() each frame to advance the motion. ```go import "github.com/charmbracelet/harmonica" proj := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 0, Z: 0}, harmonica.Vector{X: 10, Y: 20, Z: 0}, harmonica.Gravity, ) // Update each frame pos := proj.Update() ``` -------------------------------- ### Projectile Initialization Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/architecture.md Shows the function signature for creating a new Projectile object. This involves storing initial state and time delta, resulting in O(1) time complexity. ```go proj := NewProjectile(deltaTime, initialPosition, initialVelocity, acceleration) ``` -------------------------------- ### Implement Camera Follow with Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Use a spring animation to create a smooth camera follow effect, smoothly interpolating camera position towards the player's position plus an offset. ```go camera.x, camera.xVel = spring.Update(camera.x, camera.xVel, player.x + offset.x) camera.y, camera.yVel = spring.Update(camera.y, camera.yVel, player.y + offset.y) ``` -------------------------------- ### Create and Update Spring Animation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/index.md Instantiate a Spring for smooth animations. Update it each frame with the current position, velocity, and target position. ```go import "github.com/charmbracelet/harmonica" // Create once, reuse across frames spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) // Update each frame x, xVel := spring.Update(x, xVel, targetX) y, yVel := spring.Update(y, yVel, targetY) ``` -------------------------------- ### Smooth Camera Following Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/examples.md Implement smooth camera following behind a player character with an offset. This uses Harmonica's spring to create a natural, non-jerky camera movement. ```go type Camera struct { x, xVel float64 y, yVel float64 spring harmonica.Spring offsetX float64 offsetY float64 } func NewCamera(offsetX, offsetY float64) *Camera { return &Camera{ spring: harmonica.NewSpring(harmonica.FPS(60), 3.0, 0.8), offsetX: offsetX, offsetY: offsetY, } } func (c *Camera) Follow(playerX, playerY float64) { targetX := playerX + c.offsetX targetY := playerY + c.offsetY c.x, c.xVel = c.spring.Update(c.x, c.xVel, targetX) c.y, c.yVel = c.spring.Update(c.y, c.yVel, targetY) } func (c *Camera) Pos() (float64, float64) { return c.x, c.y } ``` -------------------------------- ### Color Transition Animation with Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Shows how to use a Spring to animate RGB color values. The SetColor method defines the target color, and the Update method interpolates the current color towards the target. ```go type Widget struct { r, rVel float64 g, gVel float64 b, bVel float64 spring harmonica.Spring } func (w *Widget) SetColor(r, g, b float64) { // Spring will smoothly animate to this color } func (w *Widget) Update(targetR, targetG, targetB float64) { w.r, w.rVel = w.spring.Update(w.r, w.rVel, targetR) w.g, w.gVel = w.spring.Update(w.g, w.gVel, targetG) w.b, w.bVel = w.spring.Update(w.b, w.bVel, targetB) } ``` -------------------------------- ### Use Spring for Target-Based Animations Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md For animations that need to smoothly approach a specific target value, use `spring.Update`. The `proj.Update` method is not suitable as it continues moving indefinitely. ```go pos := proj.Update() // Keeps moving; doesn't settle at target ``` ```go x, xVel = spring.Update(x, xVel, targetX) // Smoothly approaches target ``` -------------------------------- ### Under-Damped Spring Configuration Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Configure a spring with a damping ratio less than 1.0 for a bouncy, under-damped animation. This is suitable for playful UI elements. ```go // Bouncy animation (ratio = 0.3) spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.3) ``` -------------------------------- ### Match Framerate for Spring Initialization Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Ensure the framerate provided to `harmonica.NewSpring` matches your application's actual framerate for correct animation speed. Alternatively, use a measured delta time. ```go spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) // But game runs at 30 FPS → wrong animation speed ``` ```go spring := harmonica.NewSpring(harmonica.FPS(30), 6.0, 0.5) // Matches actual FPS // Or measure delta time spring := harmonica.NewSpring(measuredDeltaTime, 6.0, 0.5) ``` -------------------------------- ### Integrate with Graphics Libraries (Ebiten) Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/examples.md Use Harmonica springs to animate object positions within a graphics library like Ebiten. Render objects at their smoothed positions for visual fluidity. ```go // Ebiten integration func (g *Game) Update() { // Animate position using spring g.pos.X, g.posVel.X = g.spring.Update( g.pos.X, g.posVel.X, g.targetX, ) } func (g *Game) Draw(screen *ebiten.Image) { // Render at smoothed position ebitenutil.DrawRect(screen, g.pos.X, g.pos.Y, ...) } ``` -------------------------------- ### Spring Coefficient Storage and Transformation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-algorithm.md Shows the structure for storing spring coefficients and the 2x2 transformation matrix applied each frame to update position and velocity. ```go type Spring struct { posPosCoef, posVelCoef float64 // For position update velPosCoef, velVelCoef float64 // For velocity update } [newPos] [posPosCoef posVelCoef] [oldPos] [equilibriumPos] [newVel] = [velPosCoef velVelCoef] [oldVel] + [0 ] ``` -------------------------------- ### Manage Multiple Projectiles Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Implement a system for managing a collection of active projectiles, including firing new ones and removing those that go out of bounds. ```go var projectiles []*harmonica.Projectile func fire() { proj := harmonica.NewProjectile(fps, pos, vel, acc) projectiles = append(projectiles, proj) } func update() { for i := len(projectiles) - 1; i >= 0; i-- { pos := projectiles[i].Update() if outOfBounds(pos) { projectiles = append(projectiles[:i], projectiles[i+1:]...) } } } ``` -------------------------------- ### Converting a Point to a Vector Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/coordinates.md Shows how to convert a Point's coordinates into a Vector, representing its displacement from the origin. ```go p := harmonica.Point{X: 3, Y: 4, Z: 0} // Point as vector from origin v := harmonica.Vector{X: p.X, Y: p.Y, Z: p.Z} ``` -------------------------------- ### Import Harmonica Library Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/index.md The import path for the Harmonica Go library is provided for use in your projects. ```go import "github.com/charmbracelet/harmonica" ``` -------------------------------- ### Coordinate System Switching with TerminalGravity Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Demonstrates how to use `TerminalGravity` for screen coordinates where Y increases downward, contrasting it with standard math coordinates. ```go import ( "github.com/charmbracelet/harmonica" ) // Standard math coordinates (Y up) proj1 := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 100, Z: 0}, harmonica.Vector{X: 10, Y: 10, Z: 0}, harmonica.Gravity, ) // Screen/TUI coordinates (Y down) proj2 := harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{X: 0, Y: 100, Z: 0}, harmonica.Vector{X: 10, Y: 10, Z: 0}, harmonica.TerminalGravity, ) ``` -------------------------------- ### Initialize and Update Spring Animation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Initializes a new Spring animation with specified FPS, tension, and friction. The Update method is used to move a value towards a target, returning the new value and velocity. ```go spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) // Always moving toward a target x, xVel = spring.Update(x, xVel, targetX) ``` -------------------------------- ### Projectile Pooling for Particle Systems Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Demonstrates a `ParticlePool` struct to manage and reuse `Particle` objects, improving performance in particle effects by reducing allocations. ```go type ParticlePool struct { available []*Particle active []*Particle } func (p *ParticlePool) Get() *Particle { if len(p.available) > 0 { particle := p.available[len(p.available)-1] p.available = p.available[:len(p.available)-1] p.active = append(p.active, particle) return particle } // Create new if pool empty return &Particle{ projectile: harmonica.NewProjectile( harmonica.FPS(60), harmonica.Point{}, harmonica.Vector{}, harmonica.Gravity, ), } } ``` -------------------------------- ### NewSpring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/spring.md Initializes a new Spring by computing motion parameters for a given time step and damping characteristics. This function is used to create a reusable Spring object for animations. ```APIDOC ## NewSpring ### Description Initializes a new Spring by computing motion parameters for a given time step and damping characteristics. ### Method `NewSpring(deltaTime, angularFrequency, dampingRatio float64) Spring` ### Parameters #### Path Parameters - **deltaTime** (float64) - Required - Time step for each update, in seconds. Use `FPS(n)` for convenience. - **angularFrequency** (float64) - Required - Angular frequency of oscillation; controls speed. Higher values animate faster. Clamped to non-negative. - **dampingRatio** (float64) - Required - Damping ratio that determines oscillation behavior. Clamped to non-negative. See [Damping Behavior](#damping-behavior). ### Returns - **Spring**: A `Spring` value containing pre-computed coefficients. ### Example ```go import "github.com/charmbracelet/harmonica" // Create a 60 FPS spring with moderate speed and slight springiness spring := harmonica.NewSpring( harmonica.FPS(60), // 60 frames per second 6.0, // angular frequency (speed) 0.5, // damping ratio (springiness) ) ``` ``` -------------------------------- ### Over-Damped Spring Configuration Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Configure a spring with a damping ratio greater than 1.0 for over-damped motion. This results in a slow, smooth animation without oscillation, suitable for gentle transitions. ```go // Slow, smooth animation (ratio = 1.5) spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 1.5) ``` -------------------------------- ### Velocity-Based Rotation with Spring Smoothing Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Shows how to update a `GameObject`'s rotation to smoothly follow the direction of its velocity using `math.Atan2` and `harmonica.Spring`. ```go import "math" type GameObject struct { pos harmonica.Point vel harmonica.Vector rotation float64 rotVel float64 rotSpring harmonica.Spring } func (g *GameObject) Update() { // Compute target rotation from velocity targetRot := math.Atan2(g.vel.Y, g.vel.X) // Smooth rotation toward velocity direction g.rotation, g.rotVel = g.rotSpring.Update( g.rotation, g.rotVel, targetRot, ) } ``` -------------------------------- ### Critically Damped Spring Configuration Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Configure a spring with a damping ratio close to 1.0 for critically damped motion. This provides the fastest approach to the target without oscillation, ideal for professional UIs. ```go // Smooth, non-bouncy animation (ratio = 1.0) spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 1.0) ``` -------------------------------- ### Integrate with Game Engines (Godot) Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/examples.md Integrate Harmonica springs into a game loop using a measured delta time. This allows for smooth, physics-based movement in custom game engines. ```go // Godot/Custom engine integration func (game *MyGame) UpdateFrame(deltaTime float64) { // Use measured delta time instead of FPS() spring := harmonica.NewSpring(deltaTime, 6.0, 0.5) x, xVel = spring.Update(x, xVel, targetX) } ``` -------------------------------- ### Calculate FPS Delta Time Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/utilities.md Shows the underlying calculation for converting framerate to delta time. ```go FPS(n) = (time.Second / time.Duration(n)).Seconds() ``` -------------------------------- ### Animate Color Channel with Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Use spring animations to smoothly interpolate individual color channels (R, G, B) towards their target values. ```go r, rVel = spring.Update(r, rVel, targetR) g, gVel = spring.Update(g, gVel, targetG) b, bVel = spring.Update(b, bVel, targetB) ``` -------------------------------- ### Animating Color Channels with Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/usage-guide.md Apply spring animations to individual color channels (R, G, B) for smooth color transitions. The spring updates each channel independently. ```go import ( "image/color" "github.com/charmbracelet/harmonica" ) type ColorFloat struct { R, G, B float64 } spring := harmonica.NewSpring(harmonica.FPS(60), 4.0, 0.6) var ( currentColor ColorFloat colorVel ColorFloat ) targetColor := ColorFloat{R: 255, G: 128, B: 64} func animateColor() { currentColor.R, colorVel.R = spring.Update(currentColor.R, colorVel.R, targetColor.R) currentColor.G, colorVel.G = spring.Update(currentColor.G, colorVel.G, targetColor.G) currentColor.B, colorVel.B = spring.Update(currentColor.B, colorVel.B, targetColor.B) return color.RGBA{ R: uint8(currentColor.R), G: uint8(currentColor.G), B: uint8(currentColor.B), A: 255, } } ``` -------------------------------- ### Avoid Creating Springs Every Frame Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Do not instantiate a new `harmonica.NewSpring` within a loop. Create the spring once and reuse it for efficiency. ```go for range updateLoop { spring := harmonica.NewSpring(fps, 6.0, 0.5) // Wasteful! x, xVel = spring.Update(x, xVel, targetX) } ``` ```go spring := harmonica.NewSpring(fps, 6.0, 0.5) for range updateLoop { x, xVel = spring.Update(x, xVel, targetX) } ``` -------------------------------- ### Accessing and Modifying Point Components Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/api-reference/coordinates.md Illustrates how to access individual X, Y, and Z coordinates of a Point and modify them in place. ```go pos := harmonica.Point{X: 10, Y: 20, Z: 30} x := pos.X // 10.0 y := pos.Y // 20.0 z := pos.Z // 30.0 // Modify in place pos.X = 15 pos.Y = 25 ``` -------------------------------- ### Animate Rotation with Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Apply a spring animation to control rotation values, ensuring smooth transitions to a target rotation. ```go rotation, rotVel = spring.Update(rotation, rotVel, targetRotation) ``` -------------------------------- ### Inefficient Spring Frequency Updates Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Highlights an inefficient practice of recomputing Spring coefficients on every frame. This should be avoided by creating the Spring instance once and reusing it. ```go // ✗ Inefficient: Recomputing coefficients every frame for each frame { spring = NewSpring(FPS(60), 6.0, 0.5) // Wasteful! x, xVel = spring.Update(x, xVel, targetX) } ``` -------------------------------- ### Mismatched Framerate for Spring Animation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Demonstrates a common error where the configured FPS for a Spring does not match the actual framerate of the application, leading to incorrect animation behavior. ```go // ✗ Wrong: FPS doesn't match actual framerate spring := NewSpring(FPS(60), 6.0, 0.5) // Claims 60 FPS // But game runs at 30 FPS! ``` -------------------------------- ### Spring Type Usage Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Illustrates the usage of the `Spring` type for creating and updating animations. Note that `Spring` coefficients are immutable. ```go s := harmonica.NewSpring(delta, frequency, damping) pos, vel = s.Update(pos, vel, target) ``` -------------------------------- ### Smooth Camera Zoom with Spring Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-vs-projectile.md Implements a smooth zoom effect for a camera using a Spring. The ZoomTo method sets a new target zoom level, and the Update method animates the camera's current zoom towards it. ```go type Camera struct { zoom, zoomVel float64 spring harmonica.Spring targetZoom float64 } func (c *Camera) ZoomTo(target float64) { c.targetZoom = target } func (c *Camera) Update() { c.zoom, c.zoomVel = c.spring.Update(c.zoom, c.zoomVel, c.targetZoom) } ``` -------------------------------- ### Machine Epsilon Calculation Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/spring-algorithm.md Dynamically computes machine epsilon to adapt to the host machine's floating-point precision, enhancing numerical robustness. ```go var epsilon = math.Nextafter(1, 2) - 1 ``` -------------------------------- ### Projectile Type Usage Source: https://github.com/charmbracelet/harmonica/blob/master/_autodocs/quick-reference.md Demonstrates the creation and update cycle for the `Projectile` type, which manages mutable state for physics simulations. ```go p := harmonica.NewProjectile(delta, pos, vel, acc) newPos := p.Update() ```