Tuning game balance with parameters: difficulty without frustration
2025-12-22 ・ ~9 min read
Why parameters matter
For small games, the difference between “fun” and “frustrating” often comes down to tuning. I try to expose parameters so I can iterate quickly and keep changes understandable.
What I parameterize
- Throw power curve and clamp
- Physics materials (friction, restitution) per object
- Special mechanics triggers (e.g., every N throws)
- Camera limits and control sensitivity
GameParams Implementation Example
export const GameParams = {
// Plate settings
plate: {
radius: 0.67, // Plate radius
height: 0.033, // Plate height
thickness: 0.015, // Plate thickness
rimThickness: 0.047, // Rim collision thickness
rimWallHeight: 0.117, // Rim collision height
},
// Ball settings
ball: {
radius: 0.15, // Ball radius
mass: 0.3, // Mass
connectionDistance: 0.35, // Connection detection distance
idealConnectionDistance: 0.3, // Ideal connection distance
squashY: 1.0, // Y-axis squash amount
colliderSquashY: 0.75, // Collider Y-axis squash amount
colliderRadiusScale: 0.95, // Collider radius multiplier
},
// Physics settings
physics: {
friction: 0.25, // Friction coefficient (0.0-1.0)
restitution: 0.0, // Restitution coefficient (0.0-1.0)
linearDamping: 0.15, // Linear damping
angularDamping: 0.55, // Angular damping
contactEquationStiffness: 1e6, // Contact "hardness"
contactEquationRelaxation: 4, // Contact relaxation
frictionEquationStiffness: 1e6, // Friction "hardness"
frictionEquationRelaxation: 4, // Friction relaxation
},
// Camera settings
camera: {
distance: 3, // Distance from plate
height: 2, // Camera height
rotationSensitivity: 0.1, // Rotation sensitivity
maxRotationAngle: Math.PI / 2, // Max rotation angle (±90 degrees)
},
// Throw power settings
throw: {
speedMultiplier: 4, // Speed multiplier
minSpeed: 0.8, // Min speed
maxSpeed: 2.0, // Max speed
upFactor: 1, // Upward force multiplier
},
// Debug settings
debug: {
showColliders: false, // Whether to show collision bounds
},
} as const;
Usage Example
// Get ball radius
const ballRadius = GameParams.ball.radius;
// Get physics friction coefficient
const friction = GameParams.physics.friction;
// Calculate throw power
const speed = Math.min(
GameParams.throw.maxSpeed,
Math.max(
GameParams.throw.minSpeed,
power * GameParams.throw.speedMultiplier
)
);
Iteration workflow
- Keep params centralized (one place to tweak)
- Change one dimension at a time and playtest quickly
- Prefer small, reversible changes to avoid losing track of cause/effect