{"id":14169,"date":"2025-10-24T16:42:10","date_gmt":"2025-10-24T14:42:10","guid":{"rendered":"https:\/\/monodes.com\/predaelli\/?p=14169"},"modified":"2025-10-24T16:42:12","modified_gmt":"2025-10-24T14:42:12","slug":"how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca","status":"publish","type":"post","link":"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/","title":{"rendered":"How to emulate hand-drawn shapes \/ Algorithms behind RoughJS | shihn.ca"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<h2 class=\"wp-block-heading\"><em><a href=\"https:\/\/shihn.ca\/posts\/2020\/roughjs-algorithms\/\">How to emulate hand-drawn shapes \/ Algorithms behind RoughJS | shihn.ca<\/a><\/em><\/h2>\n\n\n\n<p>A dive into graphics algorithms used in RoughJS &#8211; A graphics library that lets you draw in a sketchy, hand-drawn-like, style.<\/p>\n<\/blockquote>\n\n\n\n<!--more-->\n\n\n\n<!--nextpage-->\n\n\n\n<p><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<h1 class=\"wp-block-heading\">How to emulate hand-drawn shapes \/ Algorithms behind RoughJS<\/h1>\n\n\n\n<p>A dive into graphics algorithms used in RoughJS &#8211; A graphics library that lets you draw in a sketchy, hand-drawn-like, style.<\/p>\n\n\n\n<p>Apr 29, 2020<\/p>\n\n\n\n<p>9 minute read<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p><a href=\"https:\/\/roughjs.com\/\">RoughJS<\/a> is a small-ish (&lt;9kB) JavaScript graphics library that lets you draw in a <em>sketchy, hand-drawn-like<\/em> style. It lets you draw on <code class=\"\" data-line=\"\">&lt;canvas&gt;<\/code> and with <code class=\"\" data-line=\"\">SVG<\/code>. This blog post is to address the most common issue filed with RoughJS: <strong>How does it work?<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"190\" data-attachment-id=\"14171\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/rough1\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?fit=648%2C242&amp;ssl=1\" data-orig-size=\"648,242\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"rough1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?fit=510%2C190&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?resize=510%2C190&#038;ssl=1\" alt=\"\" class=\"wp-image-14171\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?resize=510%2C190&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?resize=300%2C112&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?w=648&amp;ssl=1 648w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">A bit of history<\/h2>\n\n\n\n<p>Charmed by images of hand-drawn graphs, diagrams, and sketches; like I true nerd, I wondered what if there were a way to draw such figures through code. Emulate hand-drawing as close as possible and yet be legible and programmable. I decided to focus on primitives &#8211; lines, polygons, ellipses, and curves, creating a full 2D graphics library. Charting\/Diagramming libraries and apps could be built on top of it.<\/p>\n\n\n\n<p>After some quick research, I came across this paper by <a href=\"https:\/\/www.gicentre.net\/jwo\">Jo Wood<\/a> and others, titled <a href=\"https:\/\/openaccess.city.ac.uk\/id\/eprint\/1274\/\">Sketchy rendering for information visualization<\/a>. The techniques described here formed the basis of the library, especially for drawing lines and ellipses.<\/p>\n\n\n\n<p>I wrote the first version in 2017 which only worked on Canvas. Once the problem was solved, I lost interest. A year later I was working a lot with SVG, and decided to adapt RoughJS to also work with SVG. I also redesigned the API to be more basic and focus on the simple vector graphic primitives. I shared the 2.0 version of Hacker News and, surprisingly, it blew up. It was the <a href=\"https:\/\/bestofshowhn.com\/2018\">second most popular post of ShowHN<\/a> in 2018.<\/p>\n\n\n\n<p>People have since created some amazing things with RoughJS \u2014 <a href=\"https:\/\/excalidraw.com\/\">Excalidraw<\/a>, <a href=\"https:\/\/whydocatsanddogs.com\/\">Why do Cats &amp; Dogs&#8230;<\/a>, <a href=\"https:\/\/github.com\/jwilber\/roughViz\">roughViz charting library<\/a>, to name a few.<\/p>\n\n\n\n<p>Now let&#8217;s get to the algorithms&#8230;.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Roughness<\/h2>\n\n\n\n<p>The fundamental concept behind emulating hand-drawn shapes is randomness. When you draw anything by hand, no two shapes will be exactly the same. Nothing is exact. So, every spatial point in RoughJS is adjusted by a random offset. The amount of randomness is described by a numeric parameter called <code class=\"\" data-line=\"\">roughness<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"253\" data-attachment-id=\"14172\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/roughness\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/roughness.png?fit=400%2C253&amp;ssl=1\" data-orig-size=\"400,253\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"roughness\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/roughness.png?fit=400%2C253&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/roughness.png?resize=400%2C253&#038;ssl=1\" alt=\"\" class=\"wp-image-14172\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/roughness.png?w=400&amp;ssl=1 400w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/roughness.png?resize=300%2C190&amp;ssl=1 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/figure>\n\n\n\n<p>Imagine a point <code class=\"\" data-line=\"\">A<\/code> and a circle around it. <code class=\"\" data-line=\"\">A<\/code> is now replaced by a random point within that circle. The area of this circle of randomness is controlled by the <code class=\"\" data-line=\"\">roughness<\/code> value.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Lines<\/h2>\n\n\n\n<p>Hand drawn lines are never straight and often develop a <em>bowing<\/em> curvature (described <a href=\"https:\/\/openaccess.city.ac.uk\/id\/eprint\/1274\/\">here<\/a>). We randomize the two end points of the line based on the roughness. Then we also pick two other random points around the 50% and 75% marks along the line. Connecting these points by a curve gives the <em>bowing<\/em> effect.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"124\" data-attachment-id=\"14173\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/line\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/line.png?fit=600%2C146&amp;ssl=1\" data-orig-size=\"600,146\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"line\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/line.png?fit=510%2C124&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/line.png?resize=510%2C124&#038;ssl=1\" alt=\"\" class=\"wp-image-14173\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/line.png?resize=510%2C124&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/line.png?resize=300%2C73&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/line.png?w=600&amp;ssl=1 600w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<p>When drawing by hand, people sometimes go quickly back and forth on the line. This could be either to highlight the line, or just as an adjustment to the straightness of the line. It looks something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"426\" data-attachment-id=\"14174\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/hand-line\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/hand-line.jpg?fit=600%2C501&amp;ssl=1\" data-orig-size=\"600,501\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"hand-line\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/hand-line.jpg?fit=510%2C426&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/hand-line.jpg?resize=510%2C426&#038;ssl=1\" alt=\"\" class=\"wp-image-14174\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/hand-line.jpg?resize=510%2C426&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/hand-line.jpg?resize=300%2C251&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/hand-line.jpg?w=600&amp;ssl=1 600w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<p>To give this extra sketchy effect, RoughJS draws the line twice to make it feel more sketchy. I do plan to make this more configurable in the future.<\/p>\n\n\n\n<p>Try sketching lines on this interactive canvas surface. Adjust the roughness to see the lines change: Roughness0.4 <canvas width=\"600\" height=\"400\"><\/canvas><\/p>\n\n\n\n<p>When drawing by hand, longer lines tend to get less straight and more curvy. So, the randomness of the offsets to create the <code class=\"\" data-line=\"\">bowing<\/code> effect are a function of the line&#8217;s length and the <code class=\"\" data-line=\"\">randomness<\/code> value. This, however, does not scale for really long lines. In the image below, for example, concentric squares are drawn with the same random seeds &#8211; i.e. they are essentially the same random shape, but scaled differently.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"400\" data-attachment-id=\"14175\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/r7\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?fit=400%2C400&amp;ssl=1\" data-orig-size=\"400,400\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"r7\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?fit=400%2C400&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?resize=400%2C400&#038;ssl=1\" alt=\"\" class=\"wp-image-14175\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?w=400&amp;ssl=1 400w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?resize=300%2C300&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?resize=150%2C150&amp;ssl=1 150w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/figure>\n\n\n\n<p>You will notice that the edges on outer squares tend to look a bit more <code class=\"\" data-line=\"\">rough<\/code> than the inner ones. So, a dampening factor is also added based on the length of the line. The dampening factor is applied as a step function at different lengths.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"400\" data-attachment-id=\"14175\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/r7\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?fit=400%2C400&amp;ssl=1\" data-orig-size=\"400,400\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"r7\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?fit=400%2C400&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?resize=400%2C400&#038;ssl=1\" alt=\"\" class=\"wp-image-14175\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?w=400&amp;ssl=1 400w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?resize=300%2C300&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r7.jpg?resize=150%2C150&amp;ssl=1 150w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Ellipses (and circles)<\/h2>\n\n\n\n<p>Take a piece of paper and draw a bunch of circles as quickly as your can, in one continuous motion. Here&#8217;s what it looks like when I did that:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"290\" data-attachment-id=\"14176\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/circle-drawn\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/circle-drawn.jpg?fit=600%2C341&amp;ssl=1\" data-orig-size=\"600,341\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"circle-drawn\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/circle-drawn.jpg?fit=510%2C290&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/circle-drawn.jpg?resize=510%2C290&#038;ssl=1\" alt=\"\" class=\"wp-image-14176\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/circle-drawn.jpg?resize=510%2C290&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/circle-drawn.jpg?resize=300%2C171&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/circle-drawn.jpg?w=600&amp;ssl=1 600w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<p>Notice that the start and end point of the loop don&#8217;t actually meet unless you are very careful. RoughJS tries to achieve this while making it look more complete (adapted from the <a href=\"https:\/\/openaccess.city.ac.uk\/id\/eprint\/1274\/\">giCenter paper<\/a>).<\/p>\n\n\n\n<p>The algorithm finds <code class=\"\" data-line=\"\">n<\/code> points on an ellipse, where <code class=\"\" data-line=\"\">n<\/code> is determined by the size of the ellipse. Each point is then randomized by its <code class=\"\" data-line=\"\">roughness<\/code>. A curve is then fitted through these points. To achieve the <em>ends not meeting effect<\/em> the second to last point does not meet the first point. Instead, it joins the second and third points.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"512\" data-attachment-id=\"14177\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/ellipse\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/ellipse.png?fit=562%2C564&amp;ssl=1\" data-orig-size=\"562,564\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"ellipse\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/ellipse.png?fit=510%2C512&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/ellipse.png?resize=510%2C512&#038;ssl=1\" alt=\"\" class=\"wp-image-14177\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/ellipse.png?resize=510%2C512&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/ellipse.png?resize=300%2C300&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/ellipse.png?resize=150%2C150&amp;ssl=1 150w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/ellipse.png?w=562&amp;ssl=1 562w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<p>A second ellipse is also drawn to give it a more closed-loop, and extra sketchy effect.<\/p>\n\n\n\n<p>Try sketching ellipses on this interactive canvas surface. Adjust the roughness to see the shapes change: Roughness0.1 <canvas width=\"600\" height=\"400\"><\/canvas><\/p>\n\n\n\n<p>As in the case with lines, some of these artefacts get more accented if the same shape is scaled to different sizes. In ellipse, this is more noticeable because the relationship is quadratic in nature. In the image below, notice the circles have the same shape, but the outer circles look more rough.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/shihn.ca\/stuff\/posts\/roughjs\/r4.jpg?w=910&#038;ssl=1\" alt=\"Rough circles\"\/><\/figure>\n\n\n\n<p>The algorithm now auto-adjusts itself based on the size of the shape by estimating more points on the circle (<code class=\"\" data-line=\"\">n<\/code>). Following is the same set of circles generated with auto-adjust.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/shihn.ca\/stuff\/posts\/roughjs\/r5.jpg?w=910&#038;ssl=1\" alt=\"Rough circles\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Filling<\/h2>\n\n\n\n<p>A common way to fill a hand-drawn shape is using <em>hachure<\/em> lines. As in hand-drawn sketches, lines do not stay within the outlines of the shape. They are also randomized. The density, angle, width of the lines is configurable.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"267\" data-attachment-id=\"14179\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/r1\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r1.jpg?fit=600%2C314&amp;ssl=1\" data-orig-size=\"600,314\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"r1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r1.jpg?fit=510%2C267&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r1.jpg?resize=510%2C267&#038;ssl=1\" alt=\"\" class=\"wp-image-14179\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r1.jpg?resize=510%2C267&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r1.jpg?resize=300%2C157&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r1.jpg?w=600&amp;ssl=1 600w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<p>Filling squares like the example above is easy, but you get into some trouble when filling all sorts of shapes. For example, concave polygons (where angles can be more than 180\u00b0) often lead to problems like these:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/shihn.ca\/stuff\/posts\/roughjs\/r2.jpg?w=910&#038;ssl=1\" alt=\"Concave polygon with overflow\"\/><\/figure>\n\n\n\n<p>In fact the above image is from a bug report in an earlier version of RoughJS. Since then I have updated the hachure filling algorithm to an adapted version of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Flood_fill#Scanline_fill\">Scanline fill technique<\/a>.<\/p>\n\n\n\n<p>The <strong>Scan-Line Filling Algorithm<\/strong> can be used to fill any polygon. The idea is to scan the polygon using horizontal lines (scanlines). The scanlines go from the top of the polygon to the bottom. For each scanline, we determine at what points does the scanline intersect with the polygon. We arrange these intersecting points from left to right.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/shihn.ca\/stuff\/posts\/roughjs\/scanline.png?w=910&#038;ssl=1\" alt=\"Polygon scanline\"\/><\/figure>\n\n\n\n<p>As we go from one point to another, we switch from filling mode to non-filling mode; and toggle between the states as we encounter each intersection point on the scan line. There is a bit more to consider here, specifically edge cases and how to optimize the scan; more on this can be found here: <a href=\"http:\/\/www.cs.mun.ca\/av\/old\/teaching\/cg\/notes\/raster_poly.pdf\">Rasterizing polygons<\/a>, or <strong>expand the following section<\/strong> for pseudocode. Scanline Algorithm Details<\/p>\n\n\n\n<p>We define two data structures (tables) to hold data about the polygon edges.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffc First, a global Edge Table (ET) of all the edges sorted by the <code class=\"\" data-line=\"\">Y&lt;sub&gt;min&lt;\/sub&gt;<\/code> value. If the edges have the same <code class=\"\" data-line=\"\">Y&lt;sub&gt;min&lt;\/sub&gt;<\/code> values, then they are sorted by their <code class=\"\" data-line=\"\">X&lt;sub&gt;min&lt;\/sub&gt;<\/code> coordinate value.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffc Second, an Active Edge Table (AET) where we keep only the edges that intersect with the current scanline.<\/p>\n\n\n\n<p>Following describes the data structure in each row of the tables:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">interface EdgeTableEntry {\n  ymin: number;\n  ymax: number;\n  x: number; \/\/ Initialized to Xmin\n  iSlope: number; \/\/ Inverse of the slope of the line: 1\/m\n}\n\ninterface ActiveEdgeTableEntry {\n  scanlineY: number; \/\/ The y value of the scanline\n  edge: EdgeTableEntry;\n}\n<\/pre>\n\n\n\n<p>After initializing the Edge Table, we perform the following:<\/p>\n\n\n\n<p><strong>1.<\/strong> Set <em>y<\/em> to the smallest <em>y<\/em> in the ET. This represents the current scanline.<\/p>\n\n\n\n<p><strong>2.<\/strong> Initialize the AET to be an empty table.<\/p>\n\n\n\n<p><strong>3.<\/strong> Repeat the following until both AET and ET are empty:<\/p>\n\n\n\n<p><strong>(a)<\/strong> Move from ET bucket <em>y<\/em> to the AET edges whose <em>y<sub>min<\/sub> \u2264 y<\/em>.<\/p>\n\n\n\n<p><strong>(b)<\/strong> Remove from AET entries where <em>y = y<sub>max<\/sub><\/em>, then sort the AET on <em>x<\/em>.<\/p>\n\n\n\n<p><strong>(c)<\/strong> Fill in pixels on scanline <em>y<\/em> by using pairs of <em>x<\/em> coordinates from the AET.<\/p>\n\n\n\n<p><strong>(d)<\/strong> Increment <em>y<\/em> by appropriate value defined by the hachure density, i.e. the next scanline.<\/p>\n\n\n\n<p><strong>(e)<\/strong> For each non-vertical edge remaining in the AET, update <em>x<\/em> for the new <em>y<\/em><br \/>(<code class=\"\" data-line=\"\">edge.x = edge.x + edge.iSlope<\/code>)<\/p>\n\n\n\n<p>In the following interactive, each square represents a pixel. Move the vertices to change the polygon and see what pixels would traditionally get filled in.<\/p>\n\n\n\n<p>For a Hachure Fill, the scan lines are incremented in steps based on the specified hachure line density and each line is drawn using the line algorithm described above.<\/p>\n\n\n\n<p>This algorithm, however, is designed for horizontal scan-lines. To achieve various hachure angles, the algorithm first rotates the shape itself by the desired hachure angle. Scan-lines are calculated for the rotated shape. The computed lines are then rotated back by the hachure angle in the opposite direction.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"375\" data-attachment-id=\"14180\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/r3\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r3.jpg?fit=600%2C441&amp;ssl=1\" data-orig-size=\"600,441\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"r3\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r3.jpg?fit=510%2C375&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r3.jpg?resize=510%2C375&#038;ssl=1\" alt=\"\" class=\"wp-image-14180\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r3.jpg?resize=510%2C375&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r3.jpg?resize=300%2C221&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/r3.jpg?w=600&amp;ssl=1 600w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">More than hachure fills<\/h3>\n\n\n\n<p>RoughJS also supports other fill styles, but they are all derived from the same hachure algorithm. A <strong>cross-hatch<\/strong> is drawing hachure lines at an <code class=\"\" data-line=\"\">angle<\/code> and then again with <code class=\"\" data-line=\"\">angle + 90\u00b0<\/code>. A <strong>zig-zag<\/strong> tries to connect one hachure line to the previous. Draw tiny circles along the hachure lines to get a <strong>dotted<\/strong> pattern.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"190\" data-attachment-id=\"14171\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/rough1\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?fit=648%2C242&amp;ssl=1\" data-orig-size=\"648,242\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"rough1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?fit=510%2C190&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?resize=510%2C190&#038;ssl=1\" alt=\"\" class=\"wp-image-14171\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?resize=510%2C190&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?resize=300%2C112&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/rough1.png?w=648&amp;ssl=1 648w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Curves<\/h2>\n\n\n\n<p>Everything in RoughJS gets normalized to curves. Lines, Polygons, Ellipses, etc. So creating a sketchy curve is natural extension. In RoughJS you provide a set of points on the curve, and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Curve_fitting\">Curve fitting<\/a> is used to translate these into a set of Cubic Bezier Curves.<\/p>\n\n\n\n<p>Each Bezier curve has two endpoints, and two control points. By randomizing these based on <code class=\"\" data-line=\"\">roughness<\/code>, one can create sketchy curves in the same fashion.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"397\" height=\"192\" data-attachment-id=\"14182\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/sine\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/sine.png?fit=397%2C192&amp;ssl=1\" data-orig-size=\"397,192\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"sine\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/sine.png?fit=397%2C192&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/sine.png?resize=397%2C192&#038;ssl=1\" alt=\"\" class=\"wp-image-14182\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/sine.png?w=397&amp;ssl=1 397w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/sine.png?resize=300%2C145&amp;ssl=1 300w\" sizes=\"auto, (max-width: 397px) 100vw, 397px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Filling Curves<\/h3>\n\n\n\n<p>Filling curves, however, requires the opposite. Instead of normalizing everything to a curve, the curve is normalized to a polygon. Once you have a polygon, the scan-line algorithm can be used to fill the curved shape.<\/p>\n\n\n\n<p>One can sample the points on a curve at a desired rate by using the <a href=\"https:\/\/en.wikipedia.org\/wiki\/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves\">Cubic Bezier Curve equation<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"304\" height=\"194\" data-attachment-id=\"14183\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/bcurve1\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve1.png?fit=304%2C194&amp;ssl=1\" data-orig-size=\"304,194\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"bcurve1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve1.png?fit=304%2C194&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve1.png?resize=304%2C194&#038;ssl=1\" alt=\"\" class=\"wp-image-14183\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve1.png?w=304&amp;ssl=1 304w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve1.png?resize=300%2C191&amp;ssl=1 300w\" sizes=\"auto, (max-width: 304px) 100vw, 304px\" \/><\/figure>\n\n\n\n<p>Using a sampling rate based on the hachure density can give you enough points to fill the shape. But it&#8217;s not very efficient. If the section of the curve is sharp, you&#8217;d want more points. If the section of the curve is nearly a straight line, you&#8217;d want fewer points. One technique is to figure out how <em>curvy\/flat<\/em> the curve is. If it&#8217;s too curvy, split the curve into two smaller curves. If it&#8217;s flat, then just treat it as a line.<\/p>\n\n\n\n<p>The flatness of the curve is calculated using the method described in <a href=\"https:\/\/seant23.wordpress.com\/2010\/11\/12\/offset-bezier-curves\/\">this blog post<\/a>. The flatness value is compared to a tolerance value to decide whether to split the curve or not.<\/p>\n\n\n\n<p>Here&#8217;s the same curve with a tolerance level of 0.7:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"304\" height=\"194\" data-attachment-id=\"14184\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/bcurve2\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve2.png?fit=304%2C194&amp;ssl=1\" data-orig-size=\"304,194\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"bcurve2\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve2.png?fit=304%2C194&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve2.png?resize=304%2C194&#038;ssl=1\" alt=\"\" class=\"wp-image-14184\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve2.png?w=304&amp;ssl=1 304w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve2.png?resize=300%2C191&amp;ssl=1 300w\" sizes=\"auto, (max-width: 304px) 100vw, 304px\" \/><\/figure>\n\n\n\n<p>Based on the tolerance alone, this algorithm nicely provides enough points to represent a curve. It does not, however, efficiently get rid of unneeded points. A second parameter, <strong>distance<\/strong> helps with that. The technique uses the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm\">Ramer\u2013Douglas\u2013Peucker algorithm<\/a> to reduce the points.<\/p>\n\n\n\n<p>Following are the points generated with distance values of <code class=\"\" data-line=\"\">0.15<\/code>, <code class=\"\" data-line=\"\">0.75<\/code>, <code class=\"\" data-line=\"\">1.5<\/code>, and <code class=\"\" data-line=\"\">3.0<\/code><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/shihn.ca\/stuff\/posts\/roughjs\/bcurve3.png?w=910&#038;ssl=1\" alt=\"Bezier curve\"\/><\/figure>\n\n\n\n<p>Based on the <em>roughness<\/em> of the shape, one can set an appropriate value of distance. Once you have all the vertices of the polygon, curved shapes fill nicely:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"179\" data-attachment-id=\"14185\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/curves\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/curves.png?fit=605%2C212&amp;ssl=1\" data-orig-size=\"605,212\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"curves\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/curves.png?fit=510%2C179&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/curves.png?resize=510%2C179&#038;ssl=1\" alt=\"\" class=\"wp-image-14185\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/curves.png?resize=510%2C179&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/curves.png?resize=300%2C105&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/curves.png?w=605&amp;ssl=1 605w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">SVG Paths<\/h2>\n\n\n\n<p>SVG <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Tutorial\/Paths\">Paths<\/a> are very powerful and can be used to create all sorts of amazing drawings, which also makes them a bit tricky to work with.<\/p>\n\n\n\n<p>RoughJS parses the path and normalizes the path into only three operations: <strong>Move<\/strong>, <strong>Line<\/strong>, and <strong>Cubic Curve<\/strong>. (<a href=\"https:\/\/github.com\/pshihn\/path-data-parser\">path-data-parser<\/a>). Once normalized, the shape can be drawn using techniques described above to draw lines and curves.<\/p>\n\n\n\n<p>The <a href=\"https:\/\/github.com\/pshihn\/points-on-path\">points-on-path<\/a> package combines the path normalization and curve point sampling to estimate the appropriate points on the path.<\/p>\n\n\n\n<p>Following is rough estimation of points for the path <code class=\"\" data-line=\"\">M240,100c50,0,0,125,50,100s0,-125,50,-150s175,50,50,100s-175,50,-300,0s0,-125,50,-100s0,125,50,150s0,-100,50,-100<\/code><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"443\" height=\"273\" data-attachment-id=\"14186\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/bcurve5\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve5.png?fit=443%2C273&amp;ssl=1\" data-orig-size=\"443,273\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"bcurve5\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve5.png?fit=443%2C273&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve5.png?resize=443%2C273&#038;ssl=1\" alt=\"\" class=\"wp-image-14186\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve5.png?w=443&amp;ssl=1 443w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/bcurve5.png?resize=300%2C185&amp;ssl=1 300w\" sizes=\"auto, (max-width: 443px) 100vw, 443px\" \/><\/figure>\n\n\n\n<p>An SVG example I like to share often, a <em>sketchy<\/em> map of the United States:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"510\" height=\"266\" data-attachment-id=\"14187\" data-permalink=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/map\/\" data-orig-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/map.png?fit=600%2C313&amp;ssl=1\" data-orig-size=\"600,313\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"map\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/map.png?fit=510%2C266&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/map.png?resize=510%2C266&#038;ssl=1\" alt=\"\" class=\"wp-image-14187\" srcset=\"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/map.png?resize=510%2C266&amp;ssl=1 510w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/map.png?resize=300%2C157&amp;ssl=1 300w, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/map.png?w=600&amp;ssl=1 600w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Try RoughJS<\/h2>\n\n\n\n<p>Visit the <a href=\"https:\/\/roughjs.com\/\">website<\/a> or the <a href=\"https:\/\/github.com\/pshihn\/rough\">Github repo<\/a> or the <a href=\"https:\/\/github.com\/pshihn\/rough\/wiki\">API docs<\/a>. Follow the project on Twitter <a href=\"https:\/\/twitter.com\/RoughLib\">@RoughLib<\/a>.<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p class=\"excerpt\">How to emulate hand-drawn shapes \/ Algorithms behind RoughJS | shihn.ca A dive into graphics algorithms used in RoughJS &#8211; A graphics library that lets you draw in a sketchy, hand-drawn-like, style.<\/p>\n<p class=\"more-link-p\"><a class=\"more-link\" href=\"https:\/\/monodes.com\/predaelli\/2025\/10\/24\/how-to-emulate-hand-drawn-shapes-algorithms-behind-roughjs-shihn-ca\/\">Read more &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":14170,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[72,50],"tags":[],"class_list":["post-14169","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-documentations","category-javascript"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2025\/10\/cover.png?fit=480%2C480&ssl=1","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6daft-3Gx","jetpack-related-posts":[{"id":14555,"url":"https:\/\/monodes.com\/predaelli\/2025\/12\/28\/wired-elements\/","url_meta":{"origin":14169,"position":0},"title":"Wired Elements","author":"Paolo Redaelli","date":"2025-12-28","format":false,"excerpt":"Wired Elements, the perfect compation of rough.js A set of common UI elements with a hand-drawn, sketchy look. These can be used for wireframes, mockups, or just the fun hand-drawn look. The elements are drawn with enough randomness that no two renderings will be exactly the same \u2014 just like\u2026","rel":"","context":"In &quot;Javascript&quot;","block_context":{"text":"Javascript","link":"https:\/\/monodes.com\/predaelli\/category\/javascript\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":6310,"url":"https:\/\/monodes.com\/predaelli\/2019\/12\/12\/hand-drawn-border-buttons-in-css-%e2%84%82%f0%9d%95%a0%f0%9d%95%95%f0%9d%95%96%f0%9d%95%84%f0%9d%95%aa%f0%9d%95%8c%f0%9d%95%80\/","url_meta":{"origin":14169,"position":1},"title":"Hand-Drawn Border Buttons in CSS \u2013 \u2102\ud835\udd60\ud835\udd55\ud835\udd56\ud835\udd44\ud835\udd6a\ud835\udd4c\ud835\udd40","author":"Paolo Redaelli","date":"2019-12-12","format":false,"excerpt":"Hand-Drawn Border Buttons in CSS From: Hand-Drawn Border Buttons in CSS \u2013 \u2102\ud835\udd60\ud835\udd55\ud835\udd56\ud835\udd44\ud835\udd6a\ud835\udd4c\ud835\udd40 I know I will try this style sooner or later in a WordPress theme, eventually coupled with a old-style typewriter font. Hand-Drawn Border Buttons in CSS From: Hand-Drawn Border Buttons in CSS \u2013 \u2102\ud835\udd60\ud835\udd55\ud835\udd56\ud835\udd44\ud835\udd6a\ud835\udd4c\ud835\udd40 I know I\u2026","rel":"","context":"In &quot;Themes&quot;","block_context":{"text":"Themes","link":"https:\/\/monodes.com\/predaelli\/category\/themes\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1139,"url":"https:\/\/monodes.com\/predaelli\/2016\/03\/12\/sorting-algorithm-animations\/","url_meta":{"origin":14169,"position":2},"title":"Sorting Algorithm Animations","author":"Paolo Redaelli","date":"2016-03-12","format":false,"excerpt":"Sorting Algorithm Animations These pages show 8 different sorting algorithms on 4 different initial conditions. These visualizations are intended to: Show how each algorithm operates. Show that there is no best sorting algorithm. Show the advantages and disadvantages of each algorithm. Show that worse-case asymptotic behavior is not always the\u2026","rel":"","context":"In &quot;Senza categoria&quot;","block_context":{"text":"Senza categoria","link":"https:\/\/monodes.com\/predaelli\/category\/senza-categoria\/"},"img":{"alt_text":"[cml_media_alt id='1140']sorts[\/cml_media_alt]","src":"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2016\/03\/sorts-1.jpeg?resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2016\/03\/sorts-1.jpeg?resize=350%2C200 1x, https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2016\/03\/sorts-1.jpeg?resize=525%2C300 1.5x"},"classes":[]},{"id":8153,"url":"https:\/\/monodes.com\/predaelli\/2021\/02\/05\/local-cnd-emulators\/","url_meta":{"origin":14169,"position":3},"title":"Local CND emulators","author":"Paolo Redaelli","date":"2021-02-05","format":false,"excerpt":"We may discover that we want do use them: LocalCDN LocalCDN is a web browser extension that emulates Content Delivery Networks to improve your online privacy. It intercepts traffic, finds supported resources locally, and injects them into the environment. All of this happens automatically, so no prior configuration is required.\u2026","rel":"","context":"In &quot;Ethics&quot;","block_context":{"text":"Ethics","link":"https:\/\/monodes.com\/predaelli\/category\/ethics\/"},"img":{"alt_text":"","src":"https:\/\/decentraleyes.org\/images\/logo.svg?v=30062018","width":350,"height":200},"classes":[]},{"id":9401,"url":"https:\/\/monodes.com\/predaelli\/2022\/05\/26\/the-simdjson-library\/","url_meta":{"origin":14169,"position":4},"title":"The simdjson library","author":"Paolo Redaelli","date":"2022-05-26","format":false,"excerpt":"The simdjson library Parsing gigabytes of JSON per second JSON is everywhere on the Internet. Servers spend a lot of time parsing it. The simdjson library uses commonly available SIMD instructions and microparallel algorithms to break speed records.","rel":"","context":"In &quot;Agenda&quot;","block_context":{"text":"Agenda","link":"https:\/\/monodes.com\/predaelli\/category\/agenda\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":15334,"url":"https:\/\/monodes.com\/predaelli\/2026\/03\/15\/how-can-we-trust-them\/","url_meta":{"origin":14169,"position":5},"title":"How can we trust them?","author":"Paolo Redaelli","date":"2026-03-15","format":false,"excerpt":"When I found Algorithms with TypeScript I told myself \"wow, that's an interesting book!\" And the premises are really catching: This book grew out of a simple observation: most software engineers use algorithms and data structures every day, yet many feel uncertain about the fundamentals. \u2026 Algorithms with TypeScript bridges\u2026","rel":"","context":"In &quot;Ethics&quot;","block_context":{"text":"Ethics","link":"https:\/\/monodes.com\/predaelli\/category\/ethics\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts\/14169","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/comments?post=14169"}],"version-history":[{"count":2,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts\/14169\/revisions"}],"predecessor-version":[{"id":14190,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts\/14169\/revisions\/14190"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/media\/14170"}],"wp:attachment":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/media?parent=14169"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/categories?post=14169"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/tags?post=14169"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}