Simulating Titrations
In this notebook we use the interactive features of Pluto to understand one of the most common analytical techniques in chemistry: titrations. Everything below the sliders is computed and plotted in your browser as WebAssembly β no Julia server, no install.
Is this for me?
This notebook is intended for highschool students, university students studying chemistry or another natural science, or anyone curious. Basic chemistry knowledge is recommended (acids and bases, the pH scale), but short reminders are included. Feel free to give the notebook a try nonetheless.
The Basics
π The explanations are based on Titration (Wikipedia), Acid-base titration (Wikipedia) and Binnewies et al., 2016.
Titrations are used to analyse samples and determine unknown concentrations. One of the most common types is the acid-base titration. To find the concentration of an analyte, e.g. an acid like hydrochloric acid (HCl), you use a titrant solution, e.g. a base like sodium hydroxide (NaOH), with a known concentration. The base and acid react in a neutralisation reaction to form water.
$$\mathrm{H_{3}O^{+}} + \mathrm{OH^{-}} \rightarrow \mathrm{H_{2}O}$$
By measuring how much base is needed to convert the acid completely, the concentration of the acid is determined.
Is that always the case?
This brief explanation is only valid for very strong acids and bases, which dissociate completely, while weaker acids and bases do not. Only for very strong acids is the concentration of hydronium ions ($\mathrm{H_{3}O^{+}}$) equal to the concentration of acid ($\mathrm{HA}$ in general form).
$$c\mathrm{(H_3O^+)} = c\mathrm{(HA)}$$
For weaker acids the chemistry is a bit more complex, which is exactly what the interactive curve below lets you explore.
Titration Curves
To see whether the acid is completely converted, the pH value at each point of added base can be measured with a pH electrode. This allows the plotting of a titration curve. The inflection point of this curve is the equivalence point, where the acid has reacted completely with the added base.
For a strong acid (HCl) titrated with a strong base (NaOH), the neutralisation gives a neutral salt solution and the equivalence point sits at pH 7. For a weak acid (e.g. acetic acid, $\mathrm{CH_3COOH}$) the curve is different: it starts at a higher pH, has a flat buffer region where $\mathrm{pH} = \mathrm{p}K_\mathrm{a}$ at the half-equivalence point, and the equivalence point lies above pH 7.
Weaker acids do not dissociate completely; instead an equilibrium
$$\mathrm{HA} + \mathrm{H_2O} \longleftrightarrow \mathrm{A^-} + \mathrm{H_3O^+}$$
is formed, described by the acid constant $K_{\mathrm{a}}$, usually reported as
$$\mathrm{p}K\mathrm{_a} = -\log{K_{\mathrm{a}}}.$$
The lower the $\mathrm{p}K\mathrm{_a}$, the stronger the acid: very strong acids $\mathrm{p}K\mathrm{_a} < 0$, strong acids $0 < \mathrm{p}K\mathrm{_a} < 3$, medium-strong acids $3 < \mathrm{p}K\mathrm{_a} < 7$.
Here are some monoprotonic acids and their $\mathrm{p}K\mathrm{_a}$ values. Use them as a guide when setting the $\mathrm{p}K_\mathrm{a}$ slider below.
| Acid | pKa |
|---|---|
| $\mathrm{HCl}$ | $-6$ |
| $\mathrm{HSO_4^-}$ | $1.92$ |
| $\mathrm{HNO_{2}}$ | $3.15$ |
| $\mathrm{HF}$ | $3.17$ |
| $\mathrm{HCOOH}$ | $3.75$ |
| $\mathrm{CH_{3}COOH}$ | $4.76$ |
Interactive Titration Curve
Drag the sliders to set up your titration. Each slider is independent β change the acid strength ($\mathrm{p}K_\mathrm{a}$), the concentration and volume of the acid you are analysing, and the concentration of the titrant (NaOH). The curve, the current titration point and the equivalence point update live.
acid strength pKβ =
acid concentration cβ(HA) / (mol/L) =
acid volume Vβ(HA) / mL =
titrant concentration cβ(NaOH) / (mol/L) =
added volume of titrant V(NaOH) / mL =
The chemistry, as compilable numeric code
calc_pH returns the pH of the solution after v_Base_added mL of titrant have been added. It handles three regimes with hand-written analytic formulas β strong acids dissociate completely, while weak acids follow the acid-constant equilibrium before the equivalence point and behave as a weak base after it. The autoprotolysis of water ($10^{-7}$) keeps the pH finite near neutrality.
pH after adding v_Base_added mL of NaOH to the acid described by the other args.
"pH after adding `v_Base_added` mL of NaOH to the acid described by the other args."
function calc_pH(v_Base_added::Float64, pKa::Float64, c0_Acid::Float64,
v0_Acid::Float64, c0_Base::Float64)
Ka = 10.0^(-pKa)
pKb = 14.0 - pKa
Kb = 10.0^(-pKb)
if pKa <= 1.74 # very strong / strong acid: dissociates completely
c_H = 0.0
if v_Base_added * c0_Base <= v0_Acid * c0_Acid # up to the equivalence point
c_H = (c0_Acid * v0_Acid - c0_Base * v_Base_added) / (v0_Acid + v_Base_added) + 1.0e-7
else # after the equivalence point
c_OH = (c0_Base * v_Base_added - c0_Acid * v0_Acid) / (v0_Acid + v_Base_added)
c_H = 1.0e-14 / c_OH
end
return -log(10.0, c_H)
else # medium-strong / weak acid: acid-constant equilibrium
# starting concentrations of H3O+ (acid) and OH- (conjugate base at EP)
c0_H = -Ka / 2.0 + sqrt(Ka * Ka / 4.0 + Ka * c0_Acid)
c0_OH = -Kb / 2.0 + sqrt(Kb * Kb / 4.0 + Kb * c0_Acid)
n_acid = (c0_Acid - c0_H) * v0_Acid # mol of acid still to be neutralised
n_base = c0_Base * v_Base_added # mol of base added
if v_Base_added == 0.0 # at the starting point
return -log(10.0, c0_H)
elseif n_acid > n_base # buffer region, before the EP
c_H = Ka * (c0_Acid * v0_Acid / (c0_H * v0_Acid + c0_Base * v_Base_added) - 1.0)
return -log(10.0, c_H)
else # at / after the EP: weak-base solution
c_OH = (c0_Base * v_Base_added - n_acid) / (v0_Acid + v_Base_added) + c0_OH
c_H = 1.0e-14 / c_OH
return -log(10.0, c_H)
end
end
endVolume of titrant (mL) at the equivalence point: n(acid) = n(base added).
"Volume of titrant (mL) at the equivalence point: n(acid) = n(base added)."
function equivalence_volume(pKa::Float64, c0_Acid::Float64, v0_Acid::Float64, c0_Base::Float64)
if pKa <= 1.74
return c0_Acid * v0_Acid / c0_Base
else
Ka = 10.0^(-pKa)
c0_H = -Ka / 2.0 + sqrt(Ka * Ka / 4.0 + Ka * c0_Acid)
return (c0_Acid - c0_H) * v0_Acid / c0_Base
end
endlet
# end of the x-axis: twice the equivalence volume (or the slider reach)
v_end = max(2.0 * equivalence_volume(pKa, c0_Acid, v0_Acid, c0_Base), 1.0)
# sample the titration curve into flat Float64 vectors with an explicit loop
xs = Float64[]
ys = Float64[]
steps = 400
k = 0
while k <= steps
v = v_end * k / steps
push!(xs, v)
push!(ys, calc_pH(v, pKa, c0_Acid, v0_Acid, c0_Base))
k += 1
end
ev = equivalence_volume(pKa, c0_Acid, v0_Acid, c0_Base)
fig = Figure(size = (560, 380))
ax = Axis(fig[1, 1];
title = "Titration curve against NaOH",
xlabel = "Added volume of titrant V(NaOH) / mL",
ylabel = "pH value")
ax.xmin = 0.0
ax.xmax = v_end
ax.ymin = 0.0
ax.ymax = 14.0
lines!(ax, xs, ys; linewidth = 2.0, label = "titration curve")
scatter!(ax, [pointX], [calc_pH(pointX, pKa, c0_Acid, v0_Acid, c0_Base)];
color = :green, markersize = 12.0, label = "current point")
scatter!(ax, [ev], [calc_pH(ev, pKa, c0_Acid, v0_Acid, c0_Base)];
color = :red, markersize = 12.0, label = "equivalence point")
axislegend(ax; position = :rb)
fig
endCurrent titration point
pH at the current added volume: 10.83
Equivalence-point volume of titrant (mL): 24.67
Exercises
Set $\mathrm{p}K_\mathrm{a} = -6$ (a strong acid like HCl) and add base step by step. Where is the equivalence point, and where does the pH jump?
Now set $\mathrm{p}K_\mathrm{a} = 4.75$ (acetic acid). Notice the higher starting pH, the flat buffer region, and that the equivalence point now sits above pH 7.
Change the acid concentration. What changes in the curve and what stays the same? Keep an eye on the equivalence-point volume.
Before you change the acid volume or the titrant concentration, predict how the curve will move β then check.
Indicators
Another way of finding the equivalence point is a pH indicator β a compound that changes colour over a characteristic pH range. The jump in pH around the equivalence point is usually large enough that the colour change pinpoints it. A few common indicators and their ranges:
| Indicator | pH range |
|---|---|
| Methyl orange | 3.1 β 4.4 |
| Litmus | 5.0 β 8.0 |
| Bromothymol blue | 6.0 β 7.6 |
| Phenolphthalein | 8.3 β 10.0 |
| Alizarin yellow | 10.1 β 12.0 |
Compare the current pH above with these ranges: which indicator would change colour right at your equivalence point?
Appendix β Modelling the curve
Strong acids
At the start (no base added) a strong acid dissociates completely, so $c_0\mathrm{(H_3O^+)} = c_0\mathrm{(HA)}$ and $\mathrm{pH} = -\log{c_0\mathrm{(H_3O^+)}}$. While base is added, hydronium ions are consumed one for one, giving (before the equivalence point)
$$c\mathrm{(H_3O^+)} = \frac{c_0\mathrm{(HA)}\,V_0\mathrm{(HA)} - c_0\mathrm{(NaOH)}\,V_{\text{added}}}{V_0\mathrm{(HA)} + V_{\text{added}}} + 10^{-7}.$$
The $10^{-7}$ term accounts for the autoprotolysis of water near neutral pH. After the equivalence point the solution is a diluted base, so we use
$$c\mathrm{(OH^-)} = \frac{c_0\mathrm{(NaOH)}\,V_{\text{added}} - c_0\mathrm{(HA)}\,V_0\mathrm{(HA)}}{V_0\mathrm{(HA)} + V_{\text{added}}},\qquad c\mathrm{(H_3O^+)} = \frac{10^{-14}}{c\mathrm{(OH^-)}}.$$
Medium-strong and weak acids
For weak acids the acid constant $K_\mathrm{a}$ must be considered. At the starting point
$$c_0\mathrm{(H_3O^+)} = -\frac{K_{\mathrm{a}}}{2} + \sqrt{\frac{K_{\mathrm{a}}^2}{4} + K_{\mathrm{a}}\,c_0\mathrm{(HA)}}.$$
Between the starting point and the equivalence point the solution is a buffer, with
$$c\mathrm{(H_3O^+)} = K_{\mathrm{a}}\left(\frac{c_0\mathrm{(H_3O^+)}\,V_0\mathrm{(HA)}}{c_0\mathrm{(H_3O^+)}\,V_0\mathrm{(HA)} + c_0\mathrm{(NaOH)}\,V_{\text{added}}} - 1\right).$$
At and after the equivalence point the conjugate base $\mathrm{A^-}$ acts as a weak base, with
$$c_0\mathrm{(OH^-)} = -\frac{K_{\mathrm{b}}}{2} + \sqrt{\frac{K_{\mathrm{b}}^2}{4} + K_{\mathrm{b}}\,c_0\mathrm{(A^-)}},\qquad K_\mathrm{b} = \frac{10^{-14}}{K_\mathrm{a}},$$
which is added to the excess hydroxide from the added base before converting to $c\mathrm{(H_3O^+)}$. These are exactly the formulas implemented in calc_pH above.
Interested in Contributing?
There is still a lot that can be done β here are some ideas:
Di- and triprotonic acids as options (Medium)
A hidden-concentration exercise: determine the concentration from the curve and check your answer (Medium)
Titrations against weak bases, or of bases against acids (Medium)
Animate an erlenmeyer flask whose colour changes with the indicator (Hard)
using WasmMakie, PlutoUI