Homebrew's secret sauce - Precision brewing calculations for grain, hops, and water chemistry. Never guess your strike temps or IBUs again!
- ✅ Mash calculations: Strike water temps, infusion volumes, efficiency
- 🌡️ IBU formulas: Tinseth vs Rager methods with hop contribution breakdown
- 🧪 Water chemistry: Mineral adjustments and profile balancing
- ⚖️ Unit-aware: Metric/imperial conversions built-in
- 🛠️ Fully typed API with production-grade error handling
npm install @adametherzlab/brewkit
# or
bun add @adametherzlab/brewkit// REMOVED external import: import { strikeWaterTemp, calculateIBU, adjustWater } from '@adametherzlab/brewkit';
// REMOVED external import: import type { MashConfig, HopAddition } from '@adametherzlab/brewkit';
// Calculate strike water temperature
const mashConfig: MashConfig = {
grainWeight: 5.4,
waterRatio: 2.8,
targetTemp: 67,
grainTemp: 18
};
console.log(`Heat water to ${strikeWaterTemp(mashConfig)}°C`); // → 72.3°C
// Determine IBUs for recipe
const hops: HopAddition[] = [
{ name: 'Amarillo', alphaAcid: 8.5, weight: 50, boilTime: 60 },
{ name: 'Mosaic', alphaAcid: 12.2, weight: 30, boilTime: 15 }
];
const ibuResult = calculateIBU(hops, 1.055, 5.5, 'TINSETH');
console.log(`Total IBU: ${ibuResult.totalIBU.toFixed(1)}`); // → 45.6 IBUstrikeWaterTemp(config: MashConfig): number
Params: grainWeight(kg), waterRatio(L/kg), targetTemp(°C), grainTemp(°C)
Returns: Strike temperature in °C
strikeWaterTemp({ grainWeight: 4.5, waterRatio: 3, targetTemp: 65, grainTemp: 22 }); // → 70.1infusionVolume(currentVolume: number, grainWeight: number, currentTemp: number, targetTemp: number): number
Params: Current volume (L), grain weight (kg), current/target temps (°C)
Returns: Liters of boiling water needed
infusionVolume(15, 5, 63, 68); // → 4.87 LcalculateIBU(additions: HopAddition[], og: number, volumeGallons: number, method: IBUMethod): IBUResult
Params: Hop additions array, original gravity (1.XXX), batch volume (gal)
Returns: Object with total IBU and per-hop breakdown
calculateIBU([{alphaAcid: 6, weight: 30, boilTime: 60}], 1.050, 5, 'RAGER');adjustWater(source: WaterProfile, target: WaterProfile, volumeLiters: number): MineralAddition[]
Throws if: Target requires ion reduction (use RO water instead)
adjustWater(
{Ca: 50, Mg: 10, SO4: 80, Cl: 40},
{Ca: 150, Mg: 20, SO4: 300, Cl: 50},
25
);
// → [{mineral: 'CaSO4', grams: 12.4, volume: 25}]// TypeScript example
import {
strikeWaterTemp,
calculateIBU,
adjustWater,
BrewTypes,
type MashConfig,
type HopAddition
} from '@adametherzlab/brewkit';
// Configure mash for American IPA
const mashSetup: MashConfig = {
grainWeight: 6.8,
waterRatio: 2.6,
targetTemp: 66,
grainTemp: 20.5
};
const strike = strikeWaterTemp(mashSetup);
// Calculate hop schedule
const hopAdditions: HopAddition[] = [
{ name: 'Centennial', alphaAcid: 10, weight: 60, boilTime: 60 },
{ name: 'Simcoe', alphaAcid: 13.5, weight: 40, boilTime: 10 }
];
const ibu = calculateIBU(hopAdditions, 1.065, 6, BrewTypes.IBUMethod.TINSETH);
// Adjust water profile for crisp bitterness
const additions = adjustWater(
{ Ca: 75, Mg: 8, SO4: 120, Cl: 50 },
{ Ca: 150, Mg: 15, SO4: 300, Cl: 75 },
25
);-
Tinseth
Formula:IBU = (AA% * Util * Weight(g) * 7489) / (Vol(gal) * (1 + GA))
GA = (OG - 1.050)/0.2 -
Rager
Formula:IBU = (Weight(oz) * AA% * Util * 7489) / Vol(gal)
| Beer Style | Ca (ppm) | SO4 (ppm) | Cl (ppm) | SO4/Cl Ratio |
|---|---|---|---|---|
| Czech Pilsner | 50-70 | 30-50 | 20-30 | 1.5:1 |
| West Coast IPA | 100-150 | 200-400 | 50-100 | 3:1 |
| NEIPA | 100-150 | 50-100 | 150-200 | 1:2 |
| Dry Stout | 50-100 | 50-100 | 50-100 | 1:1 |
Units Gotchas
- All grain weights in kilograms
- Water volumes in liters unless specified (IBU uses gallons)
- Alpha acids as percentage (6.5% = 6.5, not 0.065)
- Temperature always in Celsius
Common Errors
RangeError: Target temp exceeds boiling→ Check unit conversion (F vs C)Invalid mineral: NaCl→ Use standardized mineral names (CaSO4, MgCl2)Negative delta for Ca→ Start with softer water if reducing minerals
See CONTRIBUTING.md for development setup and guidelines.
MIT © AdametherzLab