Skip to content

Coolpress/recon-math

Repository files navigation

recon-math

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.

npm version license


The core formula

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

Install

npm install recon-math
# or
pnpm add recon-math
# or
yarn add recon-math

Usage

import { 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

Supported syringe types

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

Worked examples

BPC-157: 5 mg vial, 2 mL bac water, 250 mcg dose

reconstitute({ vialStrengthMg: 5, bacWaterMl: 2, targetDoseMcg: 250, syringeType: 'U100' })
// → 10 units

BPC-157: same vial, 1 mL bac water, 250 mcg dose

reconstitute({ vialStrengthMg: 5, bacWaterMl: 1, targetDoseMcg: 250, syringeType: 'U100' })
// → 5 units

Same vial, same dose, half the units — because doubling the concentration halves the draw. This is the most common source of reconstitution errors.

CJC-1295/Ipamorelin blend: 10 mg vial, 2 mL bac water, 400 mcg total dose

reconstitute({ vialStrengthMg: 10, bacWaterMl: 2, targetDoseMcg: 400, syringeType: 'U100' })
// → 8 units

GLP-1 pre-mixed vial: 5 mg/mL concentration, 2.5 mg dose

import { fromConcentration } from 'recon-math';

fromConcentration({ concentrationMgPerMl: 5, targetDoseMg: 2.5, syringeType: 'U100' })
// → 50 units

API reference

reconstitute(options)

Calculates 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';
};

fromConcentration(options)

For pre-mixed vials where concentration is labeled directly.

type FromConcentrationOptions = {
  concentrationMgPerMl: number;
  targetDoseMg: number;
  syringeType: 'U100' | 'U40' | '1mL';
};

dosesPerVial(options)

Returns total doses from vial strength and dose amount.

dosesPerVial({ vialStrengthMg: number, targetDoseMcg: number }): number

halfLifeAtTime(options)

Returns 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)

steadyState(options)

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×

multiDoseRemaining(options)

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

Half-life reference

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

Disclaimer

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.


Contributing

Issues and PRs welcome. Calculation bugs get priority.

git clone https://github.com/Coolpress/recon-math.git
cd recon-math
npm install
npm test

License

MIT — see LICENSE.


Related

Releases

No releases published

Packages

 
 
 

Contributors