4-Parameter Logistic (4PL) Curve Fitting

A hands-on tutorial for biologists — no coding required.

When you test a drug at increasing concentrations, the biological response typically follows an S-shaped (sigmoidal) curve. At low doses, little happens. As the dose rises, response increases steeply. At high doses, the system saturates and response levels off.

The 4-Parameter Logistic (4PL) model captures this shape mathematically. It is the standard method for calculating IC50 and EC50 values in pharmacology and biochemical assays. This tutorial walks you through the equation, the fitting process, and lets you interact with real data.

Example dataset Real dose-response data from PubChem BioAssay AID 1619 — a screen for inhibitors of Plasmodium falciparum M17-family leucine aminopeptidase (M17LAP), a malaria drug target. Eight concentrations (1–100 µM) with % inhibition readout.

The 4PL Equation

The 4PL model describes response as a function of concentration $x$:

$$f(x) = A + \frac{D - A}{1 + \left(\dfrac{C}{x}\right)^B}$$

There are four parameters — one for each part of the curve's shape:

A
Bottom asymptote

Response at zero concentration (background / baseline signal).

B
Hill slope

Steepness of the curve. B > 1 = steep; B < 1 = shallow; B = 1 = classic Hill.

C
IC50 / EC50

Concentration at half-maximal response. The key output of most assays.

D
Top asymptote

Maximum response at saturating concentration (full inhibition / activation).

Why a log x-axis? Dose-response experiments use multiplicative concentration spacing (e.g., 1, 3, 10, 30, 100 µM). Plotting on a log scale spreads these evenly and makes the sigmoid symmetric — which is why every dose-response plot uses a logarithmic x-axis.

Interactive Curve Fitter

The plot below shows real inhibition data (red dots) for one compound from PubChem AID 1619, along with the best-fit 4PL curve (blue line). The IC50 is annotated with a dashed green line.

Bottom (A)
Hill slope (B)
IC50 (C)
Top (D)
Fit quality

Data source: PubChem BioAssay AID 1619 (SID 842727). IC50 reported by PubChem: 13.82 µM.

Parameter Playground

Move the sliders to see how each parameter changes the shape of the 4PL curve in real time.

How the Fitting Works

The curve is fitted using the Nelder-Mead algorithm — a numerical optimizer that finds the parameter values minimizing the sum of squared differences between the model and your data. No matrix algebra, no derivatives needed.

Step 1: Define the 4PL function
// Ascending form: % inhibition increases with concentration
// A = bottom, B = Hill slope, C = IC50, D = top
function fourPL(x, A, B, C, D) {
  return A + (D - A) / (1 + Math.pow(C / x, B));
}
Step 2: Measure the error (sum of squared residuals)
function sumSquares(params, conc, obs) {
  const [A, B, C, D] = params;
  if (C <= 0 || B <= 0) return 1e12; // reject impossible values
  return obs.reduce((acc, y, i) => {
    return acc + (y - fourPL(conc[i], A, B, C, D)) ** 2;
  }, 0);
}
Step 3: Auto-generate a starting guess from the data
function initialGuess(conc, obs) {
  const A = Math.min(...obs);
  const D = Math.max(...obs);
  const mid = (A + D) / 2;
  let bestIdx = 0, bestDist = Infinity;
  obs.forEach((y, i) => {
    const d = Math.abs(y - mid);
    if (d < bestDist) { bestDist = d; bestIdx = i; }
  });
  return [A, 1.0, conc[bestIdx], D]; // [A, B, C, D]
}
Step 4: Run Nelder-Mead to find the best parameters
// The optimizer tries many combinations of A, B, C, D
// and homes in on the values that minimize the error.
// Returns: { x: [A, B, C, D], fx: finalError }
const result = nelderMead(
  params => sumSquares(params, conc, obs),
  initialGuess(conc, obs)
);
Step 5: Plot with Plotly.js
Plotly.newPlot('plot', [
  // Raw data points
  { x: conc, y: obs, mode: 'markers', name: 'Data',
    marker: { size: 10, color: '#e74c3c' } },
  // Smooth fitted curve
  { x: xSmooth, y: ySmooth, mode: 'lines', name: '4PL Fit',
    line: { color: '#2980b9', width: 2.5 } }
], {
  xaxis: { type: 'log', title: 'Concentration (µM)' },
  yaxis: { title: '% Inhibition' }
});

References

  1. DeLean, A., Munson, P.J. & Rodbard, D. (1978). Simultaneous analysis of families of sigmoidal curves. Am J Physiol, 235(2):E97–102.
  2. Sebaugh, J.L. (2011). Guidelines for accurate EC50/IC50 estimation. Pharmaceutical Statistics, 10(2):128–134.
  3. Nelder, J.A. & Mead, R. (1965). A simplex method for function minimization. Computer Journal, 7(4):308–313.
  4. PubChem BioAssay AID 1619 — MLSCN: Inhibitors of P. falciparum M17LAP. Southern Research Institute.