Click here to Skip to main content
13,349,997 members (49,461 online)
Click here to Skip to main content
Add your own
alternative version


48 bookmarked
Posted 16 Feb 2011

Signature Box that Makes the Signature Look Right

, 21 Feb 2011
Rate this:
Please Sign up or sign in to vote.
A simple .NET signature box for handheld application. Usage of Bézier curves makes the resulting signature look more like the pen and paper version.


Have you ever noticed how childish and imprecise your signature looks when you write your name in a handheld signature box? The small size of the stylus compared to a standard pen, the nearly frictionless stylus-on-touch-screen interaction and the fact that the handheld is often hanging in the air instead of firmly lying on a table are three physical explanations for these ugly signatures. Another reason is the frequent input errors that are sent to your control from the touch screen. Those errors may vary between 1 and 4 pixels. Unnoticeable when clicking in the middle of a button, they are a pest when sampled in a signature box. By lowering the sampling rate and using Bézier curve interpolation, it's possible to reduce the impact of all these factors.

Without Bézier interpolation, the signature looks clumsy

Using Bézier interpolation, most sampling errors are corrected


While working on a project, a customer asked me to add a signature input box to his application. I looked around the Web to find an open source implementation of such a control and could not find any of sufficient quality. In the end, I decided to create my own and by the way, improving the concept using Bézier curves just to see how better the result could look. Making it open source was just natural to me. The project can now be found at

Bézier curves are used in many computer graphic applications. For example, true type fonts use cubic Bézier splines to render smooth character curves. Bézier interpolation is also used in 3D graphic animations to render smooth and natural movements. When used to smooth long and complex curves, it's better to use a Bézier path, which is a spline computed at every four points instead of the entire point set. Usage of cubic spline on a point set of four points is much faster than using the general Bézier recursive algorithm for the same result. Cubic spline, quadratic spline and linear interpolation are respectively used with samples of four, three and two points. When a sample contains more than four points, it becomes easier to use the general Bézier curve algorithm. For mathematical background and examples on Bézier curve, see the Wikipedia article. Animated GIFs and explanations given there are a good start on the subject.

Using the Code

Usage of the SignatureBox control is quite simple and straightforward. Just add the signature box to your application and make it appear the shape you want. The CreateImage method creates an image from the sampled points whether using Bézier or not (depending on the IsBezierEnabled property value). The Clear method is used to erase the content of the SignatureBox. So simple that there is nothing more to say about that control. That's why I'll expand on how I reduced the sampling rate of the control and how my Bézier implementation works in the next two sections.

Reducing the Sampling Rate

The algorithm judges if the current point is to be kept depending on its distance from the last point sampled using the pictureBox_MouseDown, pictureBox_MouseMouve and the pictureBox_MouseUp events. Upon pictureBox_MouseDown, the point given by the MouseEventArgs is added to the internal point list and set to the lastPoint field. On every pictureBox_MouseMove, the distance between the current point and the lastPoint is computed and if it's larger than the internal constant SAMPLING_INTERVAL, the point is kept. Then, when the pictureBox_MouseUp event occurs, a Point.Empty is added to the internal point list and set to the lastPoint field. The Point.Empty value is then interpreted by the drawing algorithm to reproduce the moment when the pen left the surface of the touch screen.

Now let's see how it's done in the code:

private const float SAMPLING_INTERVAL = 1.5f; // How far a new point 
				// must be from the previous one to be sampled.

private List<Point> points;
private Point lastPoint;

private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    this.lastPoint = new Point(e.X, e.Y);

private void pictureBox_MouseMove(object sender, MouseEventArgs e)
    Point newPoint = new Point(e.X, e.Y);
    if (Graph.Distance(this.lastPoint, newPoint) > SAMPLING_INTERVAL)
        this.lastPoint = newPoint;

private void pictureBox_MouseUp(object sender, MouseEventArgs e)

private void StopDraw()
    if (this.bezierEnabled)
        if ((this.pointCount > 0) && (this.pointCount < 4))
            Point[] p = new Point[this.pointCount];
            for (int i = 0; i < this.pointCount; i++)
                p[i] = this.points[this.points.Count - this.pointCount + i];
  , p);
    this.lastPoint = Point.Empty;
    this.pointCount = 0;

Graph.Distance is a simple distance calculation:

public static double Distance(Point a, Point b)
    return Math.Sqrt(Math.Pow(b.X - a.X, 2) + Math.Pow(b.Y - a.Y, 2));

With a distance of 1.5, it means that a point will be sampled only if it is at least 1.5 pixels away from the last sampled point. The following table shows how the pixels are sampled. Each box represents a pixel and contains the distance from the middle point which is the last point sampled:

Sampling demonstration

Bézier Général Algorithm

The general Bézier recursive algorithm is very simple. To have an idea how the recursion work, have a look at this animation from There are five points for a total of four grey initial segments.

Bézier in action, courtesy of

For each segment in the point set, a new point is computed using linear interpolation at a fraction "t" which is between 0 and 1. This operation reduces the point set by one thus reducing the number of segments by one. The operation is repeated until the algorithm is called with only two points (see the magenta segment from the above GIF animation). At this moment, instead of calling the Bezier.Interpolate method another time, the last point is linearly interpolated from the last segment and is returned. To determine the precision of the algorithm and to draw the complete curve, repeat the Bézier interpolation "n" times for which "t = 1 / n".

My explanation is rather crude and may not be as mathematically exact as we were taught during undergraduate degree. I hope my code is clear enough to remove the fog I may have created with my explanations.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;

namespace GravelInnovation.BezierSignature
    public static class Bezier
        /// ...
        public static Point[] Interpolate(int nbPoints, PointF[] points)
            float step = 1.0f / (nbPoints - 1);
            Point[] retPoints = new Point[nbPoints];
            int i = 0;
            for (float t = 0; t < 1.0f; t += step)
                PointF interpolatedPoint = InterpolatePoint(t, points)[0];
                retPoints[i] = new Point(

            PointF lastPoint = points[points.Length - 1];
            retPoints[retPoints.Length - 1] = new Point(

            return retPoints;

        private static PointF[] InterpolatePoint(float t, params PointF[] points)
            // There is only two points, return a simple linear interpolation.
            if (points.Length == 2)
                return new PointF[] {new PointF(
                    t * (points[1].X - points[0].X) + points[0].X, 
                    t * (points[1].Y - points[0].Y) + points[0].Y)};

            // For more than two points, call the Interpolate method with two
            // points to do a linear interpolation. This will reduce the 
            // number of points.
            PointF[] newPoints = new PointF[points.Length - 1];
            for (int i = 0; i < points.Length - 1; i++)
                newPoints[i] = InterpolatePoint(t, points[i], points[i + 1])[0];

            // This is where the recursion magic occurs
            return InterpolatePoint(t, newPoints);

Remark that instead of calling InterpolatePoint with "t" and two points to compute a linear interpolation, I should have created a private static Point LinearInterpolate(double t, Point p1, Point p2) method to make the whole code clearer. It's also clear that calling a recursive algorithm to resolve a third degree problem is overkill and less effective than using a cubic spline. Given the case of a signature, the overhead is not noticeable because there is only one point array. When used to smooth the movement of a 3D animation with thousands of point arrays themselves composed of thousands of points, any speed improvement in the algorithm is welcome.

Points of Interest

Using Bézier path improves significantly the quality of the signature without slowing too much the drawing rate on the control. If the functionality is unwanted, it's possible to set it off by changing the boolean property IsBezierEnabled.


  • 16th February, 2011: Initial post
  • 22nd February, 2011: Added code segments and a lot of explanations


This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


About the Author

Jean-Philippe Gravel
Systems Engineer eLogic Group
Canada Canada
This member doesn't quite have enough reputation to be able to display their biography and homepage.
Group type: Organisation

1 members

You may also be interested in...


Comments and Discussions

QuestionVisual Studio 2012 Pin
Member 104421093-Dec-13 5:33
memberMember 104421093-Dec-13 5:33 
AnswerRe: Visual Studio 2012 Pin
Jean-Philippe Gravel30-Jan-14 3:45
groupJean-Philippe Gravel30-Jan-14 3:45 
AnswerRe: Visual Studio 2012 Pin
Jean-Philippe Gravel30-Jan-14 3:48
groupJean-Philippe Gravel30-Jan-14 3:48 
GeneralMy vote of 5 Pin
Leonardo Paneque10-May-12 10:09
memberLeonardo Paneque10-May-12 10:09 
GeneralRe: My vote of 5 Pin
Jean-Philippe Gravel13-Jun-12 3:33
groupJean-Philippe Gravel13-Jun-12 3:33 
GeneralMy vote of 5 Pin
dasunnavoda22-Jan-12 23:46
memberdasunnavoda22-Jan-12 23:46 
GeneralRe: My vote of 5 Pin
Jean-Philippe Gravel13-Jun-12 3:28
groupJean-Philippe Gravel13-Jun-12 3:28 
QuestionFree is Nice, but why no Open Source? Pin
jp2code18-Aug-11 9:45
memberjp2code18-Aug-11 9:45 
AnswerRe: Free is Nice, but why no Open Source? Pin
Jean-Philippe Gravel13-Sep-11 9:42
groupJean-Philippe Gravel13-Sep-11 9:42 
QuestionProblem with this project Pin
Mirkox6813-Jun-11 5:22
memberMirkox6813-Jun-11 5:22 
AnswerRe: Problem with this project Pin
Jean-Philippe Gravel13-Jun-11 5:33
groupJean-Philippe Gravel13-Jun-11 5:33 
AnswerRe: Problem with this project Pin
Jean-Philippe Gravel15-Jun-11 6:16
groupJean-Philippe Gravel15-Jun-11 6:16 
GeneralRe: Problem with this project Pin
Mirkox6820-Jun-11 22:12
memberMirkox6820-Jun-11 22:12 
GeneralRe: Problem with this project Pin
Jean-Philippe Gravel21-Jun-11 7:29
groupJean-Philippe Gravel21-Jun-11 7:29 
GeneralMy vote of 5 Pin
Benjano22-Feb-11 10:55
memberBenjano22-Feb-11 10:55 
GeneralMy vote of 5 Pin
joelcarroll22-Feb-11 8:03
memberjoelcarroll22-Feb-11 8:03 
GeneralRe: My vote of 5 Pin
Jean-Philippe Gravel22-Feb-11 15:19
groupJean-Philippe Gravel22-Feb-11 15:19 
GeneralMy vote of 5 Pin
Gary Wheeler22-Feb-11 1:27
memberGary Wheeler22-Feb-11 1:27 
GeneralMy vote of 5 Pin
sam.hill16-Feb-11 19:56
membersam.hill16-Feb-11 19:56 
GeneralMy vote of 4 Pin
SledgeHammer0116-Feb-11 10:47
memberSledgeHammer0116-Feb-11 10:47 
GeneralRe: My vote of 4 Pin
Jean-Philippe Gravel16-Feb-11 15:15
groupJean-Philippe Gravel16-Feb-11 15:15 
GeneralNice Pin
vbfengshui16-Feb-11 7:46
membervbfengshui16-Feb-11 7:46 
GeneralRe: Nice Pin
Jean-Philippe Gravel16-Feb-11 15:25
groupJean-Philippe Gravel16-Feb-11 15:25 
GeneralRe: Nice Pin
vbfengshui16-Feb-11 15:36
membervbfengshui16-Feb-11 15:36 
GeneralGreat concept, but... Pin
Dmitry Sharygin16-Feb-11 6:12
memberDmitry Sharygin16-Feb-11 6:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.180111.1 | Last Updated 22 Feb 2011
Article Copyright 2011 by Jean-Philippe Gravel
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid