<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Reed Kongsun</title>
    <description>The latest articles on DEV Community by Reed Kongsun (@reed_kongsun_2ca95ffe52aa).</description>
    <link>https://dev.to/reed_kongsun_2ca95ffe52aa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3686761%2Fe7c8afdf-8a49-4fa5-ba19-38857e9e20a5.png</url>
      <title>DEV Community: Reed Kongsun</title>
      <link>https://dev.to/reed_kongsun_2ca95ffe52aa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reed_kongsun_2ca95ffe52aa"/>
    <language>en</language>
    <item>
      <title>How I made a printable graph paper generator print at true size</title>
      <dc:creator>Reed Kongsun</dc:creator>
      <pubDate>Wed, 04 Nov 2026 08:42:57 +0000</pubDate>
      <link>https://dev.to/reed_kongsun_2ca95ffe52aa/how-i-made-a-printable-graph-paper-generator-print-at-true-size-44j5</link>
      <guid>https://dev.to/reed_kongsun_2ca95ffe52aa/how-i-made-a-printable-graph-paper-generator-print-at-true-size-44j5</guid>
      <description>&lt;p&gt;I built &lt;a href="https://graphpaperonline.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=outreach-1" rel="noopener noreferrer"&gt;Graph Paper Online&lt;/a&gt;, a free tool that generates printable graph, dot, isometric, lined and other paper. The part that turned out to be interesting wasn't drawing a grid. It was making the grid come out of a printer at the size it claims to be.&lt;/p&gt;

&lt;p&gt;If you have ever printed a "5 mm" grid and measured it with a ruler, you know why this matters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfpfmmv08xn9m9es6jtz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfpfmmv08xn9m9es6jtz.png" alt="The Graph Paper Online interface, with paper settings next to a live graph-paper preview." width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule: one geometry, three outputs
&lt;/h2&gt;

&lt;p&gt;The site needs three things to agree with each other:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a &lt;strong&gt;preview&lt;/strong&gt; in the page,&lt;/li&gt;
&lt;li&gt;what the browser &lt;strong&gt;prints&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;the downloadable &lt;strong&gt;PDF&lt;/strong&gt; (plus PNG and SVG).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If each had its own drawing code, they would drift apart. So there is exactly one place that decides where every line goes, and the three outputs only translate its result.&lt;/p&gt;

&lt;p&gt;The engine is plain TypeScript with no DOM dependency, and everything is in &lt;strong&gt;millimetres&lt;/strong&gt;. A paper spec goes in, a flat description of the sheet comes out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified from the project's types&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Sheet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// mm&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// mm&lt;/span&gt;
  &lt;span class="nl"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}[];&lt;/span&gt;
  &lt;span class="nl"&gt;circles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}[];&lt;/span&gt;
  &lt;span class="nl"&gt;dots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}[];&lt;/span&gt;
  &lt;span class="nl"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;middle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// spec: paper kind, page size, spacing in mm, margins, ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grid, dot, isometric, coordinate, polar, lined and staff paper are all just different ways of producing &lt;code&gt;lines&lt;/code&gt;, &lt;code&gt;dots&lt;/code&gt;, &lt;code&gt;circles&lt;/code&gt; and &lt;code&gt;labels&lt;/code&gt;. Because the engine only returns numbers, it is easy to unit test. The tests check the geometry against expected positions with a tolerance of 0.1 mm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F53ntg8gulc3czmex41hi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F53ntg8gulc3czmex41hi.png" alt="Examples of graph, dot-grid, isometric and lined paper generated by the tool" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SVG: let the viewBox be millimetres
&lt;/h2&gt;

&lt;p&gt;For the preview and for SVG download, the sheet is rendered as an SVG whose &lt;code&gt;viewBox&lt;/code&gt; is in millimetres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 210 297"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"210mm"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"297mm"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two modes. For the on-screen preview the width and height are &lt;code&gt;100%&lt;/code&gt;, so the container decides the size and the drawing scales. For printing and export the width and height are written as physical &lt;code&gt;mm&lt;/code&gt; values, so the same drawing has a defined physical size.&lt;/p&gt;

&lt;p&gt;A small detail that keeps the DOM light: lines with the same stroke width and colour are merged into a single &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt;, instead of emitting one &lt;code&gt;&amp;lt;line&amp;gt;&lt;/code&gt; element per grid line.&lt;/p&gt;

&lt;h2&gt;
  
  
  PDF: page size equals paper size
&lt;/h2&gt;

&lt;p&gt;For the PDF, I use &lt;a href="https://pdf-lib.js.org/" rel="noopener noreferrer"&gt;pdf-lib&lt;/a&gt;. The two things that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The page size is the paper size converted to points (1 pt = 1/72 inch, so &lt;code&gt;pt = mm * 72 / 25.4&lt;/code&gt;). A4 becomes a page that is exactly A4, not "a bit larger with scaling".&lt;/li&gt;
&lt;li&gt;PDF's origin is the &lt;strong&gt;bottom-left&lt;/strong&gt;, while the engine's origin is top-left, so every y coordinate is flipped.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PT_PER_MM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;25.4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mm&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;PT_PER_MM&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawLine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;H&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;H&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;thickness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hexToRgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is vector, so the file stays small and sharp, and multi-page PDFs just repeat the same page drawing. The tool caps a PDF at 50 pages. pdf-lib is loaded on demand, so visitors who only preview or print never download it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part code can't fix: the print dialog
&lt;/h2&gt;

&lt;p&gt;Even with exact geometry, the final size depends on the print dialog. If it is set to "Fit to page", the printer may scale the sheet down to fit its printable area, and your 5 mm grid is now 4.8 mm.&lt;/p&gt;

&lt;p&gt;I can't force that from a web page, so the tool does two things instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it tells you to print at &lt;strong&gt;100% / Actual size&lt;/strong&gt;, and&lt;/li&gt;
&lt;li&gt;it offers an optional &lt;strong&gt;size-check ruler&lt;/strong&gt; at the bottom of the sheet (a 1 inch and a 1 cm mark), so you can measure the printed page with a real ruler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That ruler ended up being one of the most useful features, because it turns "my printout looks off" into something you can actually verify. I wrote the longer version of this advice as a guide: &lt;a href="https://graphpaperonline.com/guides/print-graph-paper-actual-size/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=outreach-1" rel="noopener noreferrer"&gt;how to print graph paper at actual size&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpl2on2exwqs6mfxlx08l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpl2on2exwqs6mfxlx08l.png" alt="Six paper sizes, PDF, PNG and SVG exports, and PDFs with up to 50 pages" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Astro&lt;/strong&gt; with static output. Pages are pre-rendered; the interactive tool is a &lt;strong&gt;Preact&lt;/strong&gt; island, and it is the only part that loads JavaScript of its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain CSS&lt;/strong&gt;, no framework. There wasn't enough UI to justify one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State in the URL hash&lt;/strong&gt; (&lt;code&gt;#s=6.35&amp;amp;p=a4...&lt;/code&gt;). Settings are shareable and work with the back button, without creating new indexable URLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers static assets&lt;/strong&gt; for hosting, deployed by GitHub Actions on push.&lt;/li&gt;
&lt;li&gt;Share images and the paper thumbnails are generated at build time (satori and resvg for the Open Graph images), so there is nothing to maintain by hand.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep one source of truth for geometry and make every output a thin translation of it.&lt;/li&gt;
&lt;li&gt;Decide on units on day one. Everything in millimetres, convert only at the edges, never in the middle.&lt;/li&gt;
&lt;li&gt;Test the geometry as numbers. Pixels are hard to assert on; coordinates are not.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;It's free and doesn't need an account: &lt;a href="https://graphpaperonline.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=outreach-1" rel="noopener noreferrer"&gt;graphpaperonline.com&lt;/a&gt;. I'm the maker, and I'd like to hear from anyone who prints a lot of paper: which layout or control is missing? And if you have seen better ways to handle print scaling from a web page, I'm interested.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>astro</category>
      <category>showdev</category>
    </item>
    <item>
      <title>5 Free Tools to Generate Default Avatars for Your Next Project</title>
      <dc:creator>Reed Kongsun</dc:creator>
      <pubDate>Wed, 31 Dec 2025 01:12:40 +0000</pubDate>
      <link>https://dev.to/reed_kongsun_2ca95ffe52aa/5-free-tools-to-generate-default-avatars-for-your-next-project-28i7</link>
      <guid>https://dev.to/reed_kongsun_2ca95ffe52aa/5-free-tools-to-generate-default-avatars-for-your-next-project-28i7</guid>
      <description>&lt;p&gt;We've all been there. You are building a new MVP, a dashboard, or a chat app, and you need some placeholder user avatars. The default gray silhouette is boring, but you don't have time to design custom assets.&lt;/p&gt;

&lt;p&gt;Here is a curated list of &lt;strong&gt;5 free tools&lt;/strong&gt; to generate default avatars. Whether you need a quick PNG download or a programmatic API, I've got you covered.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;a href="https://www.squarefacegenerators.com" rel="noopener noreferrer"&gt;Square Face Generator&lt;/a&gt; (Simplest Visual Maker)
&lt;/h3&gt;

&lt;p&gt;If you need high-quality, flat-style avatars without messing around with APIs or code, this is the easiest option to start with.&lt;/p&gt;

&lt;p&gt;I built/curated this web interface to make generating avatars as frictionless as possible. It’s a purely visual tool—you just mix and match the eyes, mouth, and colors, then download the PNG instantly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; UI Mockups, static websites, or when you want to hand-pick the "look" of your personas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Features:&lt;/strong&gt; No login, no watermark, 100% free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://www.squarefacegenerators.com" rel="noopener noreferrer"&gt;https://www.squarefacegenerators.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. DiceBear (Best for APIs)
&lt;/h3&gt;

&lt;p&gt;DiceBear is arguably the most popular avatar library for developers. It provides a powerful HTTP API that lets you generate avatars based on a seed (like a username).&lt;/p&gt;

&lt;p&gt;You can use it simply by putting a URL in your &lt;code&gt;img&lt;/code&gt; tag:&lt;br&gt;
&lt;code&gt;&amp;lt;img src="https://api.dicebear.com/7.x/pixel-art/svg?seed=JohnDoe" /&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Dynamic apps where avatars are generated programmatically based on user data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://dicebear.com" rel="noopener noreferrer"&gt;https://dicebear.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. UI Avatars (Best for Initials)
&lt;/h3&gt;

&lt;p&gt;Sometimes you don't need a face; you just need to identify the user. UI Avatars generates avatars with user initials. It's incredibly robust and requires no assets.&lt;/p&gt;

&lt;p&gt;Usage is straightforward:&lt;br&gt;
&lt;code&gt;https://ui-avatars.com/api/?name=Elon+Musk&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Professional/Business applications, admin dashboards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://ui-avatars.com" rel="noopener noreferrer"&gt;https://ui-avatars.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Multiavatar (Best for Diversity)
&lt;/h3&gt;

&lt;p&gt;If you are looking for something complex and multicultural, Multiavatar is amazing. It creates multicultural avatars representing people from multiple races, multiple cultures, and multiple age groups. The generation algorithm is quite impressive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Crypto projects (visualizing wallet hashes) or global communities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://multiavatar.com" rel="noopener noreferrer"&gt;https://multiavatar.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Boring Avatars (Best for React)
&lt;/h3&gt;

&lt;p&gt;Don't let the name fool you. Boring Avatars is a tiny React library that generates SVG-based, Bauhaus-inspired abstract avatars. It allows you to change the colors and styles directly via props.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; React/Next.js developers who want a lightweight dependency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link:&lt;/strong&gt; &lt;a href="https://boringavatars.com" rel="noopener noreferrer"&gt;https://boringavatars.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Need a &lt;strong&gt;quick, cute PNG&lt;/strong&gt; for your design? Go with &lt;strong&gt;&lt;a href="https://www.squarefacegenerators.com" rel="noopener noreferrer"&gt;Square Face Generator&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Need an &lt;strong&gt;API&lt;/strong&gt; to generate avatars for 1000 users? Go with &lt;strong&gt;DiceBear&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Need &lt;strong&gt;initials&lt;/strong&gt;? Go with &lt;strong&gt;UI Avatars&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What do you use for placeholders in your projects? Let me know in the comments! &lt;/p&gt;

</description>
      <category>design</category>
      <category>resources</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
