Musings of a Fondue

SVG X Khan Academy

I was inspired by the first person to port the SVG Doge to Khan Academy (the link might be to a clone and not the original). I deduced from the highly detailed image that it must have initially been an SVG file because it’s essentially impossible to create it using just the Khan Academy environment. Creating such a detailed image necessitates the use of vector editing software like Illustrator and Inkscape. Also, the decimal precise coordinates were a giveaway that it was imported from somewhere.

I Googled around for a copy of the Doge SVG file, and came across this one shared on Reddit.

After messing around for a bit, I was able to port a portion of it to Khan Academy.

I was not aware that this was a possibility! This is exciting because it means you can use dedicated software like Illustrator and Inkscape to generate rich images, and then use said images in HTML5 Canvas.

How to Port

A typical SVG


<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     width="200px" height="164px" viewBox="0 65.326 612 502.174" 
     enable-background="new 0 65.326 612 502.174" xml:space="preserve" 
     class="logo">
  <ellipse fill="#787f6a" class="ground" cx="283.5" cy="487.5" rx="259" ry="80"/>
  <path fill="#94d31b" class="kiwi" 
      d="M210.333,65.331
         C104.367,66.105-12.349,150.637,1.056,276.449
         c4.303,40.393,18.533,63.704,52.171,79.03
         c36.307,16.544,57.022,54.556,50.406,112.954
         c-9.935,4.88-17.405,11.031-19.132,20.015
         c7.531-0.17,14.943-0.312,22.59,4.341
         c20.333,12.375,31.296,27.363,42.979,51.72
         c1.714,3.572,8.192,2.849,8.312-3.078
         c0.17-8.467-1.856-17.454-5.226-26.933
         c-2.955-8.313,3.059-7.985,6.917-6.106
         c6.399,3.115,16.334,9.43,30.39,13.098
         c5.392,1.407,5.995-3.877,5.224-6.991
         c-1.864-7.522-11.009-10.862-24.519-19.229
         c-4.82-2.984-0.927-9.736,5.168-8.351l20.234,2.415
         c3.359,0.763,4.555-6.114,0.882-7.875
         c-14.198-6.804-28.897-10.098-53.864-7.799
         c-11.617-29.265-29.811-61.617-15.674-81.681
         c12.639-17.938,31.216-20.74,39.147,43.489
         c-5.002,3.107-11.215,5.031-11.332,13.024
         c7.201-2.845,11.207-1.399,14.791,0
         c17.912,6.998,35.462,21.826,52.982,37.309
         c3.739,3.303,8.413-1.718,6.991-6.034
         c-2.138-6.494-8.053-10.659-14.791-20.016
         c-3.239-4.495,5.03-7.045,10.886-6.876
         c13.849,0.396,22.886,8.268,35.177,11.218
         c4.483,1.076,9.741-1.964,6.917-6.917
         c-3.472-6.085-13.015-9.124-19.18-13.413
         c-4.357-3.029-3.025-7.132,2.697-6.602
         c3.905,0.361,8.478,2.271,13.908,1.767
         c9.946-0.925,7.717-7.169-0.883-9.566
         c-19.036-5.304-39.891-6.311-61.665-5.225
         c-43.837-8.358-31.554-84.887,0-90.363
         c29.571-5.132,62.966-13.339,99.928-32.156
         c32.668-5.429,64.835-12.446,92.939-33.85
         c48.106-14.469,111.903,16.113,204.241,149.695
         c3.926,5.681,15.819,9.94,9.524-6.351
         c-15.893-41.125-68.176-93.328-92.13-132.085
         c-24.581-39.774-14.34-61.243-39.957-91.247
         c-21.326-24.978-47.502-25.803-77.339-17.365
         c-23.461,6.634-39.234-7.117-52.98-31.273
         C318.42,87.525,265.838,64.927,210.333,65.331
         z M445.731,203.01
         c6.12,0,11.112,4.919,11.112,11.038
         c0,6.119-4.994,11.111-11.112,11.111
         s-11.038-4.994-11.038-11.111
         C434.693,207.929,439.613,203.01,445.731,203.01z"/>
</svg>

The bird SVG and its code is from here. This particular drawing is simple so all it’s code can fit in a few of lines. The Doge SVG is a much larger file. Below, we will be looking at one tiny part of it, the eyeshadow.

Doge's SVG code

The Doge’s eyeshadow,


M 1221.521, 790
c -20.16, 3.92, -49.334, 24, -64.668, 35.333
c -15.332, 11.333, -25.332, 18.667, -44, 28  
c -18.666, 9.333, -42.666, 29.333, -48.666, 34.667
s -0.668, 25.333, 18.666, 38
s 9.334, 26, 23.334, 32
s 24, 7.333, 58.666, 16  
c 34.668, 8.667, 56, 13.333, 81.334, 11.333
s 32.666, -3.334, 54.666, -18.667
s 32.668, -32, 44.668, -40.667
s 24, -19.333, 24.666, -32.667  
c 0.666, -13.333, -14, -24.667, -36.666, -48.667
c -22.668, -24, -44, -46, -65.334, -52
C 1246.854, 786.667, 1245.521, 785.333, 1221.521, 790
Meanings,

M - move to
c - relative curve to (equivalent of bezierVertex(cx1,cy1,cx2,cy2,x2,y2))
s - relative smooth curve to (not sure what this is, but see below for how to convert to c)
C - absolute curve to (equivalent of bezierVertex(cx1,cy1,cx2,cy2,x2,y2))

From relative to absolute coordinates

The first step is to convert the relative coordinates in the SVG to absolute coordinates.

For example this,


M 1221.521, 790
c -20.16, 3.92, -49.334, 24, -64.668, 35.333

becomes this,


Vertex( 1221.521, 790 );
bezierVertex(1201.361, 793.92, 1172.187, 814, 1156.853, 825.333);

Notice how each of the points is calculated as (1221.521 + pt.x, 790 + pt.y)
i.e. each of the points is relative to (1221.521, 790)

The catch - the reference point for the next bezier is not (1221.521, 790). It is now (1156.853, 825.333)

this,


//M 1156.853, 825.333            //this line implied, so not generated by svg
c -15.332, 11.333, -25.332, 18.667, -44, 28 

becomes this,


//Vertex(1156.853, 825.333);     //redundant line, but illustrates concept
bezierVertex(1141.521, 836.666, 1131.521, 844.0, 1112.853, 853.333);

Notice how each of the points is calculated as (1156.853 + pt.x, 825.333 + pt.y)

Smooth to regular

The second step is to convert the 2 point s-curves to 3 point c-curves. The equation below comes from this StackOverflow answer.

This,

M X0, Y0
C X1, Y1, X2, Y2, X3, Y3
S X4, Y4, X5, Y5

Becomes,

M X0, Y0
C X1, Y1, X2, Y2, X3, Y3
C XR, YR, X4, Y4, X5, Y5

Where,

XR = 2*X3 - X2
YR = 2*Y3 - Y2

All together

The Doge’s eyeshadow SVG code becomes this JavaScript code,


fill(142, 92, 39);
var eyeshadow = function(){
    beginShape();
    vertex(1221.521, 790);
    bezierVertex(1201.361, 793.92, 1172.187, 814, 1156.853, 825.333);
    bezierVertex(1141.521, 836.666, 1131.521, 844.0, 1112.853, 853.333);
    bezierVertex(1094.187, 862.666, 1070.187, 882.666, 1064.187, 888.0);
    bezierVertex(1058.187, 893.33, 1063.519, 913.333, 1082.853, 926.0);
    bezierVertex(1102.183, 938.67, 1092.187, 952.0, 1106.187, 958.0);
    bezierVertex(1120.187, 964.0, 1130.187, 965.333, 1164.853, 974.0);
    bezierVertex(1199.521, 982.667, 1220.853, 987.333, 1246.187, 985.333);
    bezierVertex(1271.517, 983.333, 1278.853, 981.999, 1300.853, 966.666);
    bezierVertex(1322.853, 951.336, 1333.521, 934.666, 1345.521, 925.999);
    bezierVertex(1357.521, 917.329, 1369.521, 906.666, 1370.187, 893.332);
    bezierVertex(1370.853, 879.999, 1356.187, 868.665, 1333.521, 844.665);
    bezierVertex(1310.853, 820.665, 1289.521, 798.665, 1268.187, 792.665);
    bezierVertex(1246.854, 786.667, 1245.521, 785.333, 1221.521, 790);
    endShape(CLOSE);
};
eyeshadow();

Scaling

Since there can be several bezier curves in a drawing, writing a script to do these calculations is essential for your sanity.

The script needs to convert the relative points to absolute points. It also needs to convert the 2 point s-curves to 3 point bezier-curves.

Good luck!

References

Comments