Kentucky seal generator

I made this app that renders a Seal of the Commonwealth of Kentucky in SVG. The motto can be updated, and the generated Seal can be saved in PNG format. These notes cover a few implementation details for how to edit and save an SVG image with JavaScript.

View the Kentucky seal generator app.


This project really got off the ground when I found this SVG Seal of Kentucky. The image is in the public domain, so I downloaded it and removed the original motto text shapes. Thanks, Wikimedia Commons!

Next the SVG gets some new nodes to contain the custom motto. The attributes text-anchor="middle" and startOffset="50%" together keep the motto where it should be centered on the paths:

    <path id="p1" d="M 175 331 A 145 145 0 1 1 488 331" />
    <path id="p2" d="M 168 331 A 132 132 0 1 0 495 331" />
  <text text-anchor="middle">
    <textPath id="motto_top" xlink:href="#p1" startOffset="50%">
  <text text-anchor="middle">
    <textPath id="motto_btm" xlink:href="#p2" startOffset="50%">

The other important textPath attributes are its id, used by Snap to update the motto contents, and xlink:href, which links each textPath to its path definition.

The two path elements define arc-shaped paths for the motto to follow, basically two semicircles, as shown here:

The seal shown with highlighted lines added where the text is located on the seal.

Changing the motto

Snap.svg will help display the seal and edit its motto. The first task for Snap is to load the blank Seal SVG from an external file. Contents will be added to the empty SVG element on the HTML page with id svg_home:

  viewBox="0 0 662 662"
  preserveAspectRatio="xMinYMin meet"
  <!-- contents of the blank seal svg loaded here -->
Snap.load("seal.svg", function(data) {
  // call Snap.add(data) on the outer SVG element

After the image is loaded, Snap wraps the SVG element and allows the textPath nodes to be identified:

let s = Snap("#svg_home")

let topText = s.select("#motto_top")
let btmText = s.select("#motto_btm")

Finally, the motto can be updated by setting #text attributes with Snap, suitable for wiring to an input event listener function:

function updateOnInput(topValue, btmValue) {
  topText.attr({ "#text": topValue })
  btmText.attr({ "#text": btmValue })

Save as...

Setting up the motto live-editing was easy with Snap, but saving the result isn't as straightforward. Even if you get the source of the SVG, it's still in SVG format, which isn't the nicest for viewing and sharing.

Fortunately, we can rely on browser APIs and the SVG.toDataURL and canvg libraries for SVG-to-PNG conversion without external tools or server requests.

When the 'Get image' button is clicked, this code inside the click event callback gets the SVG data, following from the example documentation for SVG.toDataURL:

sealEl.node.toDataURL("image/png", {
  callback: function(data) {
    // set the image source to data and
    // do some other stuff to handle display
    // of the image at this time.

Also note that Snap's Element.node is used to identify the seal SVG (in this case, a nested SVG element) and get a reference to the DOM object to convert with toDataURL.


Looking back, the uws-dwf app evolved in two main phases:

  1. Prototype of SVG-changing with Snap to check how easy it would be.

    • result: easy indeed, once you know how and where to draw the paths.
  2. Integrating other libraries to handle PNG conversion.

Not much else to it. #simplegifts

Links and resources

Go back to the homepage