The math behind peptide vial reconstitution. Pure TypeScript, zero dependencies.
This library is the calculation engine inside My Pep Calc. We open-sourced the math so anyone can verify it, embed it, or audit it.
Reconstituting a lyophilized peptide vial produces a solution with a known concentration. Every dose you draw is a fraction of that solution.
Concentration (mcg/mL) = Vial strength (mcg) ÷ Bac water added (mL)
Volume to draw (mL) = Target dose (mcg) ÷ Concentration (mcg/mL)
Units on U100 syringe = Volume (mL) × 100
Collapsed to one step:
Units = (Target dose ÷ Vial strength) × Bac water volume × 100
npm install recon-math
# or
pnpm add recon-math
# or
yarn add recon-mathimport { reconstitute, dosesPerVial, syringeUnits } from 'recon-math';
// Full reconstitution calculation
const result = reconstitute({
vialStrengthMg: 5, // vial label: 5 mg
bacWaterMl: 2, // you added 2 mL bac water
targetDoseMcg: 250, // your target dose: 250 mcg
syringeType: 'U100', // U100, U40, or '1mL'
});
console.log(result);
// {
// concentrationMcgPerMl: 2500,
// volumeMl: 0.1,
// syringeUnits: 10, // draw to the 10-unit mark
// dosesPerVial: 20,
// syringeType: 'U100',
// }
// Doses remaining in a vial
const doses = dosesPerVial({ vialStrengthMg: 5, targetDoseMcg: 250 });
// 20
// Convert mL to units for a specific syringe type
const units = syringeUnits({ volumeMl: 0.1, syringeType: 'U100' });
// 10| Type | Units per mL | Use case |
|---|---|---|
U100 |
100 | Standard US insulin syringe — most common for peptides |
U40 |
40 | Common in some countries; 40 units = 1 mL |
1mL |
— | Tuberculin syringe; result returned in mL, not units |
reconstitute({ vialStrengthMg: 5, bacWaterMl: 2, targetDoseMcg: 250, syringeType: 'U100' })
// → 10 unitsreconstitute({ vialStrengthMg: 5, bacWaterMl: 1, targetDoseMcg: 250, syringeType: 'U100' })
// → 5 unitsSame vial, same dose, half the units — because doubling the concentration halves the draw. This is the most common source of reconstitution errors.
reconstitute({ vialStrengthMg: 10, bacWaterMl: 2, targetDoseMcg: 400, syringeType: 'U100' })
// → 8 unitsimport { fromConcentration } from 'recon-math';
fromConcentration({ concentrationMgPerMl: 5, targetDoseMg: 2.5, syringeType: 'U100' })
// → 50 unitsCalculates all values from vial + bac water inputs.
type ReconstitutionOptions = {
vialStrengthMg: number; // vial label in mg (e.g. 5 for a 5 mg vial)
bacWaterMl: number; // bac water added in mL
targetDoseMcg: number; // target dose in mcg
syringeType: 'U100' | 'U40' | '1mL';
};
type ReconstitutionResult = {
concentrationMcgPerMl: number;
volumeMl: number;
syringeUnits: number | null; // null for '1mL' syringe type
volumeMlDisplay: number; // always populated
dosesPerVial: number;
syringeType: 'U100' | 'U40' | '1mL';
};For pre-mixed vials where concentration is labeled directly.
type FromConcentrationOptions = {
concentrationMgPerMl: number;
targetDoseMg: number;
syringeType: 'U100' | 'U40' | '1mL';
};Returns total doses from vial strength and dose amount.
dosesPerVial({ vialStrengthMg: number, targetDoseMcg: number }): numberReturns remaining active mass at time hoursElapsed after a single dose.
import { halfLifeAtTime } from 'recon-math';
halfLifeAtTime({ initialDoseMcg: 250, halfLifeHours: 4, hoursElapsed: 8 })
// → 62.5 (25% remaining after 2 half-lives)Steady-state peak/trough/average for repeated dosing.
import { steadyState } from 'recon-math';
steadyState({ doseMcg: 10000, halfLifeHours: 120, intervalHours: 168 })
// tirzepatide 10mg weekly: peak ~17.7mg, accumulation factor ~1.77×Total remaining mass at a given time from a series of past doses (uses superposition).
import { multiDoseRemaining } from 'recon-math';
multiDoseRemaining({
doses: [
{ hoursFromStart: 0, doseMcg: 250 },
{ hoursFromStart: 4, doseMcg: 250 },
],
halfLifeHours: 4,
evaluateAtHours: 8,
})
// → 187.5| Compound | Half-life | Typical dosing |
|---|---|---|
| BPC-157 | ~4 hours | Once or twice daily |
| CJC-1295 (no DAC) | ~30 minutes | Once or twice daily |
| Ipamorelin | ~2 hours | Once or twice daily |
| CJC-1295 (with DAC) | ~6–8 days | Once or twice weekly |
| TB-500 | ~24 hours | Loading/maintenance |
| Tirzepatide | ~5 days | Once weekly |
| Semaglutide | ~7 days | Once weekly |
This library performs arithmetic. It does not provide medical advice, recommend dosages, or endorse any compound. The user is responsible for confirming inputs (vial strength, bac water volume, target dose) with a licensed healthcare provider before acting on any output.
Issues and PRs welcome. Calculation bugs get priority.
git clone https://github.com/Coolpress/recon-math.git
cd recon-math
npm install
npm testMIT — see LICENSE.
- My Pep Calc — the full multi-compound tracker that uses this library
- Reconstitution calculator — web UI for these calculations
- Learn: How to reconstitute a peptide vial — the math explained