tool / handwritingcss · scroll · react
Handwriting animation generator
Type a name or a short line and get an SVG that writes itself, stroke by stroke, in the order a pen would. The letters come from a single-stroke font, so every line is drawn once. Set the speed, the pauses and a loop, then download the file, take the scroll version, or copy it as React.
- Fonts
- 3
- Characters
- 95
- Outputs
- 3
- Uploads
- 0
Write your line
The sample is “Ada Lovelace” in the script font: 13 strokes and 11 letters, written in 3.8s. The preview is the file you download, playing in this page. Nothing is uploaded: the text is laid out in your browser.
13 strokes · 11 letters
<!-- Made with https://svg-scroll-draw.vercel.app/handwriting-animation-generator -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 392 54" width="392" height="54" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="handwriting" role="img" aria-label="Ada Lovelace">
<style>
/* Every stroke has pathLength="1", so it is 1 long in dash units:
a dash of 1 and a gap of 2, pushed back by 1, hide it; 0 draws it. */
/* --at is when the pen reaches the stroke, --for how long it takes. */
.handwriting path {
stroke-dasharray: 1 2;
animation: handwriting-draw linear both;
animation-duration: var(--for);
animation-delay: var(--at);
}
@keyframes handwriting-draw {
from { stroke-dashoffset: 1; }
to { stroke-dashoffset: 0; }
}
/* Less motion asked for: the finished line, at once. */
@media (prefers-reduced-motion: reduce) {
.handwriting path {
animation: none;
stroke-dasharray: none;
}
}
</style>
<path pathLength="1" style="--at:0s;--for:0.463s" d="M3.25 50.5L5.5 49.38Q7.75 48.25 11.13 44.88Q14.5 41.5 17.88 37Q21.25 32.5 25.75 24.63Q30.25 16.75 33.63 10L37 3.25L37 50.5L35.88 47.13Q34.75 43.75 32.5 40.38Q30.25 37 28 34.75Q25.75 32.5 22.38 30.25Q19 28 16.75 28Q14.5 28 13.38 29.13Q12.25 30.25 12.25 32.5Q12.25 34.75 13.38 37Q14.5 39.25 16.75 41.5Q19 43.75 22.38 46Q25.75 48.25 29.13 49.38Q32.5 50.5 38.13 50.5L43.75 50.5"/>
<path pathLength="1" style="--at:0.513s;--for:0.248s" d="M68.5 37L67.38 34.75Q66.25 32.5 64 31.38Q61.75 30.25 59.5 30.25Q57.25 30.25 55 31.38Q52.75 32.5 51.63 33.63Q50.5 34.75 49.38 37Q48.25 39.25 48.25 41.5Q48.25 43.75 49.38 46Q50.5 48.25 52.75 49.38Q55 50.5 57.25 50.5Q59.5 50.5 61.75 49.38Q64 48.25 65.13 46Q66.25 43.75 73 23.5L79.75 3.25"/>
<path pathLength="1" style="--at:0.761s;--for:0.1s" d="M70.75 30.25L69.63 35.88Q68.5 41.5 68.5 44.88Q68.5 48.25 69.63 49.38Q70.75 50.5 71.88 50.5Q73 50.5 75.25 49.38Q77.5 48.25 78.63 47.13Q79.75 46 82 42.63L84.25 39.25"/>
<path pathLength="1" style="--at:0.911s;--for:0.277s" d="M104.5 37L103.38 34.75Q102.25 32.5 100 31.38Q97.75 30.25 95.5 30.25Q93.25 30.25 91 31.38Q88.75 32.5 87.63 33.63Q86.5 34.75 85.38 37Q84.25 39.25 84.25 41.5Q84.25 43.75 85.38 46Q86.5 48.25 88.75 49.38Q91 50.5 93.25 50.5Q95.5 50.5 97.75 49.38Q100 48.25 101.13 46Q102.25 43.75 104.5 37L106.75 30.25L105.63 35.88Q104.5 41.5 104.5 44.88Q104.5 48.25 105.63 49.38Q106.75 50.5 107.88 50.5Q109 50.5 111.25 49.38Q113.5 48.25 114.63 47.13Q115.75 46 118 42.63L120.25 39.25"/>
<path pathLength="1" style="--at:1.538s;--for:0.404s" d="M165.25 30.25L167.5 30.25Q169.75 30.25 174.25 29.13Q178.75 28 182.13 25.75Q185.5 23.5 187.75 21.25Q190 19 191.13 16.75Q192.25 14.5 192.25 11.13Q192.25 7.75 191.13 5.5Q190 3.25 187.75 3.25Q185.5 3.25 184.38 4.38Q183.25 5.5 182.13 7.75Q181 10 179.88 15.63Q178.75 21.25 177.63 26.88Q176.5 32.5 175.38 35.88Q174.25 39.25 173.13 41.5Q172 43.75 169.75 46Q167.5 48.25 165.25 49.38Q163 50.5 160.75 50.5Q158.5 50.5 157.38 49.38Q156.25 48.25 156.25 46Q156.25 43.75 157.38 42.63Q158.5 41.5 160.75 41.5Q163 41.5 165.25 42.63Q167.5 43.75 170.88 46Q174.25 48.25 177.63 49.38Q181 50.5 183.25 50.5Q185.5 50.5 188.88 49.38Q192.25 48.25 194.5 46L196.75 43.75"/>
<path pathLength="1" style="--at:1.992s;--for:0.242s" d="M212.5 30.25L210.25 30.25Q208 30.25 205.75 31.38Q203.5 32.5 202.38 33.63Q201.25 34.75 200.13 37Q199 39.25 199 41.5Q199 43.75 200.13 46Q201.25 48.25 203.5 49.38Q205.75 50.5 208 50.5Q210.25 50.5 212.5 49.38Q214.75 48.25 215.88 47.13Q217 46 218.13 43.75Q219.25 41.5 219.25 39.25Q219.25 37 218.13 34.75Q217 32.5 214.75 31.38L212.5 30.25L211.38 31.38Q210.25 32.5 210.25 34.75Q210.25 37 211.38 39.25Q212.5 41.5 214.75 42.63Q217 43.75 220.38 43.75Q223.75 43.75 226 42.63Q228.25 41.5 229.38 40.38L230.5 39.25"/>
<path pathLength="1" style="--at:2.284s;--for:0.152s" d="M230.5 39.25L235 30.25L233.88 35.88Q232.75 41.5 232.75 44.88Q232.75 48.25 233.88 49.38Q235 50.5 236.13 50.5Q237.25 50.5 240.63 49.38Q244 48.25 246.25 46Q248.5 43.75 249.63 40.38Q250.75 37 250.75 33.63L250.75 30.25"/>
<path pathLength="1" style="--at:2.436s;--for:0.055s" d="M250.75 30.25L251.88 34.75Q253 39.25 254.13 40.38Q255.25 41.5 257.5 41.5Q259.75 41.5 262 40.38L264.25 39.25"/>
<path pathLength="1" style="--at:2.541s;--for:0.176s" d="M266.5 46L268.75 44.88Q271 43.75 272.13 42.63Q273.25 41.5 274.38 39.25Q275.5 37 275.5 34.75Q275.5 32.5 274.38 31.38Q273.25 30.25 272.13 30.25Q271 30.25 268.75 31.38Q266.5 32.5 265.38 34.75Q264.25 37 264.25 40.38Q264.25 43.75 265.38 46Q266.5 48.25 268.75 49.38Q271 50.5 273.25 50.5Q275.5 50.5 277.75 49.38Q280 48.25 281.13 47.13Q282.25 46 284.5 42.63L286.75 39.25"/>
<path pathLength="1" style="--at:2.768s;--for:0.278s" d="M286.75 39.25L289 35.88Q291.25 32.5 294.63 26.88Q298 21.25 299.13 19Q300.25 16.75 301.38 13.38Q302.5 10 302.5 7.75Q302.5 5.5 301.38 4.38L300.25 3.25L298 4.38Q295.75 5.5 294.63 7.75Q293.5 10 292.38 14.5Q291.25 19 290.13 26.88Q289 34.75 289 41.5Q289 48.25 290.13 49.38Q291.25 50.5 292.38 50.5Q293.5 50.5 295.75 49.38Q298 48.25 299.13 47.13Q300.25 46 302.5 42.63L304.75 39.25"/>
<path pathLength="1" style="--at:3.096s;--for:0.277s" d="M325 37L323.88 34.75Q322.75 32.5 320.5 31.38Q318.25 30.25 316 30.25Q313.75 30.25 311.5 31.38Q309.25 32.5 308.13 33.63Q307 34.75 305.88 37Q304.75 39.25 304.75 41.5Q304.75 43.75 305.88 46Q307 48.25 309.25 49.38Q311.5 50.5 313.75 50.5Q316 50.5 318.25 49.38Q320.5 48.25 321.63 46Q322.75 43.75 325 37L327.25 30.25L326.13 35.88Q325 41.5 325 44.88Q325 48.25 326.13 49.38Q327.25 50.5 328.38 50.5Q329.5 50.5 331.75 49.38Q334 48.25 335.13 47.13Q336.25 46 338.5 42.63L340.75 39.25"/>
<path pathLength="1" style="--at:3.422s;--for:0.151s" d="M356.5 34.75L356.5 33.63Q356.5 32.5 355.38 31.38Q354.25 30.25 352 30.25Q349.75 30.25 347.5 31.38Q345.25 32.5 344.13 33.63Q343 34.75 341.88 37Q340.75 39.25 340.75 41.5Q340.75 43.75 341.88 46Q343 48.25 345.25 49.38Q347.5 50.5 350.88 50.5Q354.25 50.5 357.63 48.25Q361 46 363.25 42.63L365.5 39.25"/>
<path pathLength="1" style="--at:3.624s;--for:0.176s" d="M367.75 46L370 44.88Q372.25 43.75 373.38 42.63Q374.5 41.5 375.63 39.25Q376.75 37 376.75 34.75Q376.75 32.5 375.63 31.38Q374.5 30.25 373.38 30.25Q372.25 30.25 370 31.38Q367.75 32.5 366.63 34.75Q365.5 37 365.5 40.38Q365.5 43.75 366.63 46Q367.75 48.25 370 49.38Q372.25 50.5 374.5 50.5Q376.75 50.5 379 49.38Q381.25 48.25 382.38 47.13Q383.5 46 385.75 42.63L388 39.25"/>
</svg>One file, no JavaScript: open it, use it as an <img>, or paste it inline. As an image, currentColor is black, so pick a colour above; inline, it follows the text colour. With reduced motion asked for, it shows the finished line.
Why a single-stroke font
A line-drawing animation draws a path’s stroke. A normal font’s letter is a filled shape, and its path is the shape’s edge, so drawing the stroke traces round the outside of every line: the word comes out double (fig. 1, top). Handwriting needs paths that are the pen’s centre line, one per pen stroke, which is what a single-stroke font is.
The fonts here are the Hershey fonts, drawn by Dr. A. V. Hershey at the U.S. National Bureau of Standards in 1967 for pen plotters, as lists of points the pen moves through. Each glyph is a few strokes in the order he drew them, and the script letters end where the next one starts, so a word joins up. Laid out left to right, the strokes are already in writing order.
How the pen is timed
Every stroke has pathLength="1", so its dash is 1 long whatever its real length, and one rule draws them all: stroke-dasharray: 1 2, the offset from 1 to 0. What makes it handwriting is the timing. The pen moves at one speed, so each stroke takes time in proportion to its length; it lifts for the letter stagger between letters and for the word pause at a space. Each stroke carries its start and its length of time as two custom properties:
<!-- Each stroke: its own start (--at) and length of time (--for). -->
<path pathLength="1" style="--at:0s;--for:0.463s" d="M3.25 50.5L5.5 49.38 …"/>
<path pathLength="1" style="--at:0.513s;--for:0.248s" d="M68.5 37L67.38 34.75 …"/>This is the whole stylesheet the generator writes for “Ada”:
/* Made with https://svg-scroll-draw.vercel.app/handwriting-animation-generator */
/* Every stroke has pathLength="1", so it is 1 long in dash units:
a dash of 1 and a gap of 2, pushed back by 1, hide it; 0 draws it. */
/* --at is when the pen reaches the stroke, --for how long it takes. */
.handwriting path {
stroke-dasharray: 1 2;
animation: handwriting-draw linear both;
animation-duration: var(--for);
animation-delay: var(--at);
}
@keyframes handwriting-draw {
from { stroke-dashoffset: 1; }
to { stroke-dashoffset: 0; }
}
/* Less motion asked for: the finished line, at once. */
@media (prefers-reduced-motion: reduce) {
.handwriting path {
animation: none;
stroke-dasharray: none;
}
}@keyframes over the whole cycle instead, named in --n. With one shared rule and a delay, every stroke would restart on its own schedule and the line would never clear at once.On time or on scroll
The SVG file plays over time, when it loads. To write as the reader scrolls instead, the On scroll tab gives the same strokes to scrollDraw, and the React tab wraps them in <ScrollDraw>. Here is how each setting carries over.
| Setting | The CSS file | scrollDraw |
|---|---|---|
| duration | The seconds of ink. Each stroke takes its share by length, so the pen keeps one speed: a long loop takes longer than a dot. | None: the scroll sets the pace. The line starts when its top is 85% down the window and its last stroke lands at 35%. |
| letter stagger | Seconds the pen lifts between one letter and the next, added to the next letter’s --at. | stagger: 1, so each stroke starts as the one before it ends: the same order, an equal stretch of scroll each. |
| word pause | Seconds the pen lifts at a space, in place of the stagger. | Not carried over: every stroke gets the same stretch. |
| loop | Write, hold 1.5s, fade out over 0.5s, again: each stroke its own keyframes over the cycle. | Scrolling back unwrites it; once: true keeps it written. |
| reduced motion | A media query in the file: no animation, no dash, the finished line. | scrollDraw shows the line written. |
| pathLength | pathLength="1" on every stroke, so one rule fits them all. | Left off: scrollDraw measures each stroke itself. |
Use it in your project
Handwriting animation questions
How do I make a handwriting animation with CSS?
Write the text as SVG paths that follow the pen, not as a font’s outlines, and animate each path’s stroke-dashoffset from its length to 0, one path after another. With pathLength="1" on every path the dash values are all 1, so one @keyframes rule draws any stroke, and each path only needs its own animation-delay and animation-duration. The generator above writes those paths from a single-stroke font and works out every delay from the pen’s speed.
Why does my text drawing animation draw double lines?
Because the font is an outline font, which is almost every font. Its letters are filled shapes, and a stroke animation follows the edge of the shape, so each line of the letter comes out as two parallel lines, the outline traced all the way round. Handwriting needs a single-stroke font, where each path is the centre line of the pen. This tool uses the Hershey fonts, made for pen plotters in the 1960s and in the public domain.
How do I animate a signature in SVG?
Type the name, choose the script font, and download the SVG: it carries its own CSS, so it writes itself when it loads, as an inline SVG or as an <img>. For a real scanned signature, trace it as one path per pen stroke and use the SVG line drawing generator, which times any SVG the same way.
Can the animated signature play when it scrolls into view, or with the scroll?
The On scroll tab gives the same SVG to scrollDraw from svg-scroll-draw with stagger: 1, so the strokes write one after another as the page scrolls, starting when the line is 85% down the window, and unwrite when it scrolls back. The React tab is the same as a <ScrollDraw> component.
Does the handwriting animation respect reduced motion?
Yes. The SVG file has a prefers-reduced-motion media query inside it that turns the animation off and the dash off, so the finished text shows at once; in Chromium and Firefox that holds even when the file is an <img>. The scroll version shows the text written too, because scrollDraw finishes the drawing when reduced motion is asked for.
Which characters and fonts are there?
The 95 printable ASCII characters, in 3 fonts: Script, Roman, Roman italic. Roman italic is the roman slanted. Letters with accents are written without them, curly quotes and dashes are straightened, and anything else is left out with a note.
Can I use the generated SVG commercially?
Yes. The Hershey fonts were created by Dr. A. V. Hershey at the U.S. National Bureau of Standards and are in the public domain, and the code the generator writes is yours. Nothing is uploaded: the text is laid out in your browser.
See a hand-traced signature written on scroll in the signature example, or read how SVG line drawing works in CSS. All of svg-scroll-draw 2.27.3: the docs.