Gear Drawing with Bézier Curves


Spur gear tooth profiles are shaped as circle involute curves. The involute is generated from its base circle as if a taut line were unwound from the circumference, the end of that line would describe a circle involute. The involute is a transcendental function usually drawn by calculating coordinates of many points along the curve and plotting straight line segments between them.

In an effort to simplify the drafting of circular involute functions, Fumitaka Higuchi et al [1] developed a method of approximating the involute using Bézier curves. The result is a smooth, quite accurate approximation, suitable for CAD. The Bézier curve is defined by just a few control points and maintains it shape under 3D transformation. This greatly reduces the computational load required for drafting.

Set out here is a brief description of the Higuchi method, along with a JavaScript implementation. The accuracy of the approximation is calculated and examples of drawing gears with the Bézier curves are shown.

Circle involute parametric equations

Fig 1 shows a graphical representation of how the involute profile for a gear tooth is generated. Click on the red dot and drag the 'taut' line as it unwraps from the blue base circle. The dot traces out a circle involute.

Figure 1. Schematic diagram of gear showing involute profile (magenta) and its base circle (blue). Drag the red dot to demostrate the involute generation geometry.

The Cartesian coordinates of a point on the involute may be expressed in parametric form using the generating angle \( \theta \) as a parameter. Click here to show the construction lines of the derivation (click again to hide them).

From the diagram, point \( x',y' \) is at radius \( R_b \) and angle \( \theta \), therefore:

$$\begin{align} x' &= R_b\, \cos(\theta)\\ y' &= R_b\, \sin(\theta) \end{align}$$

The line c, as it unwinds from the circle, is always tangential to the circumference and the radius \( R_b \) is always perpendicular to c. Therefore:

$$\begin{align} x &= x' + c\, \sin(\theta)\\ y &= y' - c\, \cos(\theta) \end{align}$$

The involute is the locus of the end of a string being 'unwound' from the base circle. This implies:

$$ c' = c = R_b \theta $$

Therefore, the parametric equations for the involute, to be approximated with Bézier curves, are:

$$\begin{align} x &= R_b \cos(\theta) + R_b \theta \sin(\theta) \tag{1}\\ y &= R_b \sin(\theta) - R_b \theta \cos(\theta) \end{align}$$

Involute gear tooth profile dimensions

The geometry of a gear is set by the following basic factors:

Module value, \( m \),
Number of gear teeth \( Z \),
Pressure angle \( \phi \).

The pitch circle diameter \( D \), involute base circle radius \( R_b \) and addendum circle radius \( R_a \) are related by the formulae:

$$\begin{align} D &= m*Z\\ R_b &= D/2 \, \cos(\phi)\\ R_a &= D/2 + m \end{align}$$

The involute gear profile starts at the base circle and ends where the involute meets the tip circle, also known as the addendum circle. The value of the involute generating parameter \( \theta \) starts at \( 0 \) on the base circle and ends at value, \( \theta_a \), which may be calculated the schematic diagram shown in Fig. 2 as follows:

Figure 2. Schematic diagram of involute gear tooth showing the polar coordinates of the involute profile (magenta) and its base circle (blue).

The Cartesian coordinates of a point on the involute are given at eqn 1. Substituting the polar coordinates of the point, \( (R, \psi) \), results in the expression:

$$\begin{align} R \cos(\psi) &= R_b \cos(\theta) + R_b \theta \sin(\theta)\\ R \sin(\psi) &= R_b \sin(\theta) - R_b \theta \cos(\theta) \end{align}$$

Squaring both sides and adding:

$$\begin{align} R^2 &= R_b^2 (1 + \theta^2)\\ \theta &= \frac{\sqrt{R^2 - R_b^2}}{R_b} \end{align}$$

Hence the value of \( \theta \) at the addendum, the outer radius of the gear teeth, is given by

$$\theta_a = \frac{\sqrt{R_a^2 - R_b^2}}{R_b}$$

Also useful in Higuchi's approximation method, is an expression for the distance along the involute, \( s \), as a function of \( \theta \).

$$s(\theta) = \frac{R_b\theta^2}{2} $$

Higuchi et al involute approximation method

The first step in the Higuchi method [1] is to approximate the circle involute curve using the Chebyshev approximation formula which expresses the curve as a truncated series of polynomials. This requires mapping θ onto the -1..+1 range expected by the Chebyshev formula. The terms of the series are then recombined to represent the Bernstein polynomial form (the basis of Bézier curves). A further parameter mapping of the Chebyshev parameter onto the 0..1 range for the Bézier parameter is required.

The radius of curvature of the involute varies along its length, starting from zero at its on the base circle. This singularity generates a corresponding singularity in the Bézier approximation, resulting in a double control point at the base circle. Higuchi suggests avoiding this wasted node by beginning the approximation a short distance from the base circle, say 1% of the total length.

Higuchi applies this method to a typical gear, having module, 3mm, 17 teeth and pressure angle 25°. Approximation errors are reported for Bézier approximations of order 4, 6 and 8. These errors are typically a few parts in 106, 109 and 1012 respectively when normalised by the diametral pitch.

JavaScript implementation

A JavaScript implementation of the Higuchi method was written and the source code is available in the file gearUtils-05.js. This implementation handles any order Bézier curve from 3 upward, with arbitrary start and end points along the involute.

The JavaScript utility provides the function:

involuteBezCoeffs(module, numTeeth, pressureAngle, order, fstart, fstop)

The required parameters are module, the metric gear size, and numTeeth, the number of teeth. The optional parameters are: pressureAngle (defaults to 20°), order, the order of the Bézier approximation (defaults to 3), fstart, the start offset as a proportion of the involute profile length from start to addendum (defaults to 0.01) and fstop, the stop offset as a fraction of the distance to addendum (defaults to 1). The function returns an array of JavaScript objects of the form {x:, y:} representing a x,y coordinates of the Bézier curve nodes. For an order N approximation, there will be N+1 nodes in the array; the start point, N-1 control points and the end point.

Cubic Bézier approximation

The Higuchi technique would be of great benefit in web based gear modelling if it produced accurate approximations using cubic (order 3) Bézier curves, as the HTML5 canvas element has native support only for quadratic and cubic Bézier curves.

To test the method's performance, a cubic Bézier curve fit was made to a typical gear profile. The specifications of the gear used in the example are the same as used in Higuchi's paper, (module=3, teeth=17, pressureAngle=25). Fig. 3 shows a plot of the cubic Bézier curve approximation to the involute (green line). A piece-wise plot of the true involute, is shown for comparison (magenta line). The Bézier curve control points are shown as crosses.

Figure 3. Cubic Bézier approximation to a circular involute (green line) calculated using the Higuchi-Chebyshev approximation method. The true involute is shown for comparison (magenta line).

Cubic Bézier errors

Table 1 shows the approximation errors for the cubic Bézier approximation to the involute shown in Fig. 3. The errors are calculated at intervals along the involute. Column 1 shows the distance along the profile as a fraction of total length. Column 2 shows the errors as the absolute value of the distance of the point on the approximation to the closest point on the true involute, measured in millimetres. Column 3 shows these error values normalised to the pitch diameter.

along involute
Error/Diametral Pitch

Table 1. Approximation errors for a single cubic Bézier curve approximation to the involute shown in Fig. 3. Each error is the distance from a point on the approximation to the closest point on the true involute.

Using a cubic Bézier curve to approximate the full profile length results in normalised errors of a few parts in 104. The worst errors occur at the extremities, not accurate enough for gear design work.

Two cubic Bézier approximation

The solution to reducing the approximation error is to split the involute into two sections fitting a cubic Bézier curve to each. This reduces the errors by an order of magnitude, to just of a few parts in 105.

The junction point of the two Bézier curves is set 25% along the involute, this helps apportion the curvature to be modelled between the two curves. The first cubic Bézier starts 1% of the way along the involute to avoid the duplicated node at the involute singularity. The start point of the second Bézier coincides with the end point of the first Bézier, so one of these nodes may be discarded. The resulting Bézier approximation has 7 nodes, start, mid and end points and 4 control points.

Here is an example using the 'involuteBezCoeffs' function to generate the two cubic Bézier curves representing a gear profile. The gear parameters are the same as the example in the Higuchi paper.

var module = 3;
var teeth = 17;
var pressureAngle = 25;
var Rpitch = module*teeth/2;
var Rb = Rpitch * Math.cos(pressureAngle*Math.PI/180); // base circle radius
// generate Higuchi involute approximation
var fs = 0.01;  // start 1% off the base circle
var fm = 0.25;  // break 25% along involute
var fe = 1;     // end at 100%
var dedBz = involuteBezCoeffs(module, teeth, pressureAngle, 3, fs, fm);
var addBz = involuteBezCoeffs(module, teeth, pressureAngle, 3, fm, fe);
var data = ["M", dedBz[0].x, dedBz[0].y,
            "C", dedBz[1].x, dedBz[1].y, dedBz[2].x, dedBz[2].y, dedBz[3].x, dedBz[3].y,
            "C", addBz[1].x, addBz[1].y, addBz[2].x, addBz[2].y, addBz[3].x, addBz[3].y];
// draw the involute using Cango library
g.drawPath(data, -Rpitch, 0, {strokeColor:'blue'});

Fig. 4 shows a plot of the two cubic Bézier approximation to a circular involute (blue line) calculated by dividing the involute into two sections and fitting a cubic Bézier to each section using the Higuchi-Chebyshev approximation method. For comparison the magenta line is the true involute.

Figure 4. A two cubic Bézier approximation to the same circular involute shown in Fig. 3 (blue line). The true involute, is shown in magenta for comparison.

Two cubic Bézier errors

Table 2 shows the approximation errors at various points along the involute. Column 1 shows the distance along the involute expressed as a fraction of the total length. Column 2 shows the approximation error, the absolute value of distance of a point from a Bézier curve measured in millimetres. Column 3 shows these values normalised to the gear's pitch circle diameter.

along involute
Error/Diametral Pitch

Table 2. Approximation errors for a two cubic Bézier curve approximation. Each error is the distance to the closest point on the true involute.

The two cubic Bézier approximation provides a quick, accurate method of drawing involute gear teeth suitable for 3D manipulation.

Drawing an spur gear tooth with Cango

Having established the profile of the tooth face, the full tooth shape may be generated by the addition of an arc across the tip of the tooth, the mirror image of the tooth profile and then root fillets and the arc across the root circle to the next tooth.

The angular spacing between teeth, pitch angle, for a gear with Z teeth is:

pitch angle = \( 2\pi/Z \)

Drawing involute profiles is simplified by using the polar coordinate form of the involute equation. This expression is readily determined from Fig. 2:

Using the cosine rule of the triangle in Fig. 2:

$$\begin{align} R_b^2 + R^2 - 2R_b\, R\, \cos(\theta - \psi) &= c^2\\ &= R^2 - R_b^2\\ \cos(\theta - \psi) &= \frac{R_b}{R} \end{align}$$

Since \(c = R_b \theta\)

$$ \psi(R) = \frac{\sqrt{R^2 - R_b^2}}{R_b} - \cos^{-1}(\frac{R_b}{R}) $$

Shown below is the JavaScript source code to create a set of drawing commands for a single gear tooth outline. The output of this function is an array of drawing commands in SVG format that can be used to create a Cango PATH object representing the outline of a single gear tooth. This Cango object can be duplicated and rotated and appended to create all the teeth on a gear. This code is included in the 'gearUtils' file.

createGearTooth = function(module, teeth, pressureAngle)
  function genInvolutePolar(Rb, R)  // Rb = base circle radius
    // returns the involute angle as function of radius R.
    return (Math.sqrt(R*R - Rb*Rb)/Rb) - Math.acos(Rb/R);

  function rotate(pt, rads)  // rotate pt by rads radians about origin
    var sinA = Math.sin(rads);
    var cosA = Math.cos(rads);
    return {x: pt.x*cosA - pt.y*sinA,
            y: pt.x*sinA + pt.y*cosA};

  function toCartesian(radius, angle)   // convert polar coords to cartesian
    return {x: radius*Math.cos(angle),
            y: radius*Math.sin(angle)};
  // ****** external gear specifications
  var m = module,                                 // Module = mm of pitch diameter per tooth
      Z = teeth,                                  // Number of teeth
      phi = pressureAngle || 20,                  // pressure angle (degrees)
      addendum = m,                               // pitch circle to tip circle distance
      dedendum = 1.25*m,                          // pitch circle to root, sets clearance
      clearance = dedendum - addendum,
      // Calculate radii
      Rpitch = Z*m/2,                             // pitch circle radius
      Rb = Rpitch*Math.cos(phi*Math.PI/180),      // base circle radius
      Ra = Rpitch + addendum,                     // tip (addendum) circle radius
      Rroot = Rpitch - dedendum,                  // root circle radius
      fRad = 1.5*clearance,                       // fillet radius, max 1.5*clearance
      Rf,                                         // radius at top of fillet
      // ****** calculate angles (all in radians)
      pitchAngle = 2*Math.PI/Z,                   // angle subtended by whole tooth (rads)
      baseToPitchAngle = genInvolutePolar(Rb, Rpitch),
      pitchToFilletAngle = baseToPitchAngle,      // profile starts at base circle
      filletAngle = Math.atan(fRad/(fRad+Rroot)), // radians
      fe, fs, fm,
      dedBz, addBz, inv, invR,
      fillet, filletR, filletNext,
      rootR, rootNext,
      pt, i, data;

  Rf = Math.sqrt((Rroot+fRad)*(Rroot+fRad)-(fRad*fRad)); // radius at top of fillet
  if (Rb < Rf)
    Rf = Rroot+clearance;
  if (Rf > Rb)                  // start profile at top of fillet (if its greater)
    pitchToFilletAngle -= genInvolutePolar(Rb, Rf);
  // ****** generate Higuchi involute approximation
  fe = 1;                       // fraction of profile length at end of approx
  fs = 0.01;                    // fraction of length offset from base to avoid singularity
  if (Rf > Rb)
    fs = (Rf*Rf-Rb*Rb)/(Ra*Ra-Rb*Rb); // offset start to top of fillet
  // approximate in 2 sections, split 25% along the involute
  fm = fs+(fe-fs)/4;                  // fraction of length at junction (25% along profile)
  dedBz = involuteBezCoeffs(m, Z, phi, 3, fs, fm);
  addBz = involuteBezCoeffs(m, Z, phi, 3, fm, fe);
  // join the 2 sets of coeffs (skip duplicate mid point)
  inv = dedBz.concat(addBz.slice(1));
  //create the back profile of tooth (mirror image)
  invR = [];                          // involute profile along back of tooth
  for (i=0; i<inv.length; i++)
    // rotate all points to put pitch point at y = 0
    pt = rotate(inv[i], -baseToPitchAngle-pitchAngle/4);
    inv[i] = pt;
    // generate the back of tooth profile nodes, mirror coords in X axis
    invR[i] = {x:pt.x, y:-pt.y};
  // ****** calculate section junction points R=back of tooth, Next=front of next tooth)
  fillet = toCartesian(Rf, -pitchAngle/4-pitchToFilletAngle);       // top of fillet
  filletR = {x:fillet.x, y:-fillet.y};          // flip to make same point on back of tooth
  rootR = toCartesian(Rroot, pitchAngle/4+pitchToFilletAngle+filletAngle);
  rootNext = toCartesian(Rroot, 3*pitchAngle/4-pitchToFilletAngle-filletAngle);
  filletNext = rotate(fillet, pitchAngle);      // top of fillet, front of next tooth
  // ****** create the drawing command data array for the tooth
  data = [];
  data.push("M", fillet.x, fillet.y);           // start at top of fillet
  if (Rf < Rb)
    data.push("L", inv[0].x, inv[0].y);         // line from fillet up to base circle
  data.push("C", inv[1].x, inv[1].y, inv[2].x, inv[2].y, inv[3].x, inv[3].y,
                 inv[4].x, inv[4].y, inv[5].x, inv[5].y, inv[6].x, inv[6].y);
  data.push("A", Ra, Ra, 0, 0, 0, invR[6].x, invR[6].y); // arc across addendum circle
  data.push("C", invR[5].x, invR[5].y, invR[4].x, invR[4].y, invR[3].x, invR[3].y,
                 invR[2].x, invR[2].y, invR[1].x, invR[1].y, invR[0].x, invR[0].y);
  if (Rf < Rb)
    data.push("L", filletR.x, filletR.y);       // line down to top of fillet
  if (rootNext.y > rootR.y)         // is there a section of root circle between fillets?
    data.push("A", fRad, fRad, 0, 0, 1, rootR.x, rootR.y);// back fillet
    data.push("A", Rroot, Rroot, 0, 0, 0, rootNext.x, rootNext.y); // root circle arc
  data.push("A", fRad, fRad, 0, 0, 1, filletNext.x, filletNext.y);

  return data;  // return an array of Cango (SVG) format draw commands

Drawing gears with Cango

To create the outline of a full gear from the profile of the tooth, the Cango PATH object representing one tooth is created, this is then duplicated, rotated by the pitch angle and appended to the gear object to make each of the gear's teeth. This gear outline then has a circular axle shaft hole appended, its then ready to be rendered to the screen and animated. The code snippet to create a gear using Cango is shown below:

var m = module,               // Module = mm of pitch diameter per tooth
    Zg = gearTeeth,
    phi = pressureAngle || 20,
    Rg = Zg*m/2;              // gear Pitch radius

// generate gear
data = createGearTooth(m, Zg, phi);
gearTooth = new Cobj(data, "SHAPE", {
  border: true,
  strokeColor: "#606060" });
gearTooth.rotate(180/Zg);     // rotate gear 1/2 tooth to mesh
gear = gearTooth.dup();
for (i=1; i<Zg; i++)
  newTooth = gearTooth.dup();
  gear.appendPath(newTooth, true);  // trim move command = true
// add axle hole
Dsg = 0.6*Rg;             // diameter of gear shaft
shaft = new Cobj(shapeDefs.circle(Dsg), "PATH");
gear.appendPath(shaft);       // retain the 'moveTo' command for shaft sub path

Adding backlash when drawing meshed gears

In practical gearing there is always a gap between the non-drive face of the pinion tooth and the adjacent wheel tooth to prevent gears from jamming. This gap is termed backlash. If the direction of rotation is reversed, there is a period during which the pinion moves independently until the backlash gap is taken up and gear tooth contact re-established.

Backlash may be created by cutting the gear spaces a little deeper, so the gaps are wider than the teeth, this method is preferred for stock gears. Alternatively, backlash may be introduced by increasing the center distance between the gears. This separation does not affect the speed ratio between the gears or require any alteration to the gear tooth profile.

Recommended backlash is:

backlash = 0.04*Module
To create this backlash the center distance is increased by ΔC where:
ΔC = backlash/2 * tan(phi);

Fig. 5 shows a pair of gears with module value of 5 mm. The pinion has 24 teeth and the gear 52, the pressure angle is 20°. The involute profiles were calculated using the 2 cubic Bézier approximation. Backlash of 0.2 mm was added by increasing the center distance by 0.275 mm.

Figure 5. A spur gear animation drawn with the two cubic Bézier approximation to the involute gear tooth profiles.

Internal gear drawing example

Internal gears, or ring gears, may be drawn with similar efficiency. Fig. 6 shows an internal gear and pinion with module value of 5 mm. The pinion has 22 teeth and the ring gear 42, the pressure angle is 20°. The involute profiles were calculated using the 2 cubic Bézier approximation. Backlash of 0.2 mm was added by increasing the center distance.

Figure 6. An internal gear animation, drawn with the two cubic Bézier approximation to the involute gear tooth profiles.

The Higuchi method has proven to be applicable to gear drawing on the HTML5 canvas element despite the limitation to order 3 Bézier approximation that the canvas imposes. All the source code used on this page is released free for non-commercial use.

1. F.Higuchi, S. Gofuku, T. Maekawa, H. Mukundan, N.M. Patrikalakis, "Approximation of Involute Curves for CAD-System Processing" YNU Digital Eng Lab Memorandum 05-1 2006.
2. G.M.Maitra, Handbook of Gear Design, 1994