diff --git a/pd/doc/5.reference/draw-help.pd b/pd/doc/5.reference/draw-help.pd
index 021c4168225ff5846ced57181040e51854f587a5..b782e59c9824916b84cb2a6c0aa6c69622608104 100644
--- a/pd/doc/5.reference/draw-help.pd
+++ b/pd/doc/5.reference/draw-help.pd
@@ -1,10 +1,10 @@
 #N struct draw-help-struct float x float y;
-#N canvas 270 52 555 619 10;
+#N canvas 212 53 555 619 10;
 #X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
 14 -228856 -66577 0;
 #X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header draw 3 12 0 18 -204280
 -1 0;
-#X obj 0 339 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+#X obj 0 309 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
 -228856 -1 0;
 #N canvas 494 296 482 332 META 0;
 #X text 12 115 LIBRARY internal;
@@ -17,13 +17,13 @@ rx ry;
 #X text 12 135 AUTHOR Jonathan Wilkes;
 #X text 13 155 HELP_PATCH_AUTHORS Jonathan Wilkes;
 #X restore 500 597 pd META;
-#X obj 0 436 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+#X obj 0 406 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
 13 -228856 -1 0;
-#X obj 0 473 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+#X obj 0 443 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
 0 13 -228856 -1 0;
-#X obj 0 573 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+#X obj 0 543 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
 0 13 -228856 -1 0;
-#X obj 78 347 cnv 17 3 80 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X obj 78 317 cnv 17 3 80 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
 #N canvas 212 516 428 108 Related_objects 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
@@ -34,37 +34,37 @@ rx ry;
 #X obj 162 36 drawsymbol;
 #X obj 232 36 plot;
 #X restore 101 597 pd Related_objects;
-#X text 99 445 float;
-#X obj 78 445 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+#X text 99 415 float;
+#X obj 78 415 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
 -162280 0;
-#X text 169 445 - outputs the stored value as a float message.;
+#X text 169 415 - outputs the stored value as a float message.;
 #X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
 ;
 #X text 11 20 draw an svg shape to represent a scalar;
 #X obj 492 12 draw;
-#X text 101 554 float;
-#X text 171 525 - [draw] accepts a list of coordinates and/or shape
+#X text 101 524 float;
+#X text 171 495 - [draw] accepts a list of coordinates and/or shape
 data used to specify where and how to draw the object;
-#X scalar draw-help-struct 322 277 \;;
-#X obj 117 64 struct draw-help-struct float x float y;
-#X msg 128 201 stroke-width \$1;
-#X floatatom 128 177 5 0 0 0 - - -, f 5;
-#X floatatom 128 238 5 0 0 0 - - -, f 5;
-#X msg 128 262 transform skewx \$1;
-#X text 98 346 float;
-#X text 168 347 - any nonzero number will display the drawing to represent
+#X scalar draw-help-struct 322 250 \;;
+#X obj 117 59 struct draw-help-struct float x float y;
+#X msg 128 174 stroke-width \$1;
+#X floatatom 128 150 5 0 0 0 - - -, f 5;
+#X floatatom 128 211 5 0 0 0 - - -, f 5;
+#X msg 128 235 transform skewx \$1;
+#X text 98 316 float;
+#X text 168 317 - any nonzero number will display the drawing to represent
 the corresponding scalar. SSending a "0" will hide it.;
-#X text 98 386 [draw] also takes a number of messages. These are svg
+#X text 98 356 [draw] also takes a number of messages. These are svg
 attributes that define how the object is drawn. See the subpatch above
 for a full list.;
-#X text 81 490 1) symbol;
-#X text 81 525 n) symbol;
-#X text 107 539 or;
-#X text 171 490 - name of an svg shape. Can be circle \, ellipse \,
+#X text 81 460 1) symbol;
+#X text 81 495 n) symbol;
+#X text 107 509 or;
+#X text 171 460 - name of an svg shape. Can be circle \, ellipse \,
 line \, path \, polygon \, polyline \, rectangle \, or group.;
-#X obj 80 293 draw circle 40 40;
-#X obj 299 158 draw rect 80 80 100 -40;
-#X msg 80 112 fill red \, stroke blue;
+#X obj 80 266 draw circle 40 40;
+#X obj 299 138 draw rect 80 80 100 -40;
+#X msg 80 92 fill red \, stroke blue;
 #N canvas 326 113 587 553 more_messages 0;
 #X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
 14 -204280 -1 0;
@@ -125,8 +125,13 @@ line \, path \, polygon \, polyline \, rectangle \, or group.;
 #X connect 30 0 32 0;
 #X connect 31 0 32 0;
 #X connect 33 0 32 0;
-#X restore 299 112 pd more_messages;
-#X msg 89 137 fill blue \, stroke black;
+#X restore 299 92 pd more_messages;
+#X msg 89 117 fill blue \, stroke black;
+#X obj 172 553 pddp/pddplink drawarray-help.pd -text draw array;
+#X text 101 555 See also:;
+#X obj 172 573 pddp/pddplink drawimage-help.pd -text draw image;
+#X obj 242 553 pddp/pddplink drawsprite-help.pd -text draw sprite;
+#X obj 242 573 pddp/pddplink ./drawsvg-help.pd -text draw svg;
 #X connect 19 0 30 0;
 #X connect 20 0 19 0;
 #X connect 21 0 22 0;
diff --git a/pd/doc/5.reference/drawarray-help.pd b/pd/doc/5.reference/drawarray-help.pd
new file mode 100644
index 0000000000000000000000000000000000000000..d7b16103717e042bb1d83dead56ff8b0235606cb
--- /dev/null
+++ b/pd/doc/5.reference/drawarray-help.pd
@@ -0,0 +1,73 @@
+#N struct draw-array-help-element float y;
+#N struct draw-array-help-struct float x float y array a draw-array-help-element
+;
+#N canvas 245 53 555 619 10;
+#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
+14 -228856 -66577 0;
+#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header draw\ array 3 12
+0 18 -204280 -1 0;
+#X obj 0 339 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+-228856 -1 0;
+#N canvas 494 296 482 332 META 0;
+#X text 12 115 LIBRARY internal;
+#X text 12 25 LICENSE SIBSD;
+#X text 12 5 KEYWORDS control GUI data-structure;
+#X text 12 45 DESCRIPTION draw an svg shape to represent a scalar;
+#X text 12 65 INLET_0 float fill fill-opacity fill-rule stroke stroke-dasharray
+stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width
+rx ry;
+#X text 12 135 AUTHOR Jonathan Wilkes;
+#X text 13 155 HELP_PATCH_AUTHORS Jonathan Wilkes;
+#X restore 500 597 pd META;
+#X obj 0 436 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+13 -228856 -1 0;
+#X obj 0 473 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+0 13 -228856 -1 0;
+#X obj 0 573 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+0 13 -228856 -1 0;
+#X obj 78 347 cnv 17 3 80 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#N canvas 212 516 428 108 Related_objects 0;
+#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
+14 -204280 -1 0;
+#X text 7 1 [draw] Related Objects;
+#X obj 27 35 drawcurve;
+#X obj 92 36 drawnumber;
+#X obj 162 36 drawsymbol;
+#X obj 232 36 plot;
+#X restore 101 597 pd Related_objects;
+#X text 99 445 float;
+#X obj 78 445 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#X text 169 445 - outputs the stored value as a float message.;
+#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
+;
+#X text 101 554 float;
+#X text 171 525 - [draw] accepts a list of coordinates and/or shape
+data used to specify where and how to draw the object;
+#X text 98 346 float;
+#X text 168 347 - any nonzero number will display the drawing to represent
+the corresponding scalar. SSending a "0" will hide it.;
+#X text 98 386 [draw] also takes a number of messages. These are svg
+attributes that define how the object is drawn. See the subpatch above
+for a full list.;
+#X text 81 490 1) symbol;
+#X text 81 525 n) symbol;
+#X text 107 539 or;
+#X text 171 490 - name of an svg shape. Can be circle \, ellipse \,
+line \, path \, polygon \, polyline \, rectangle \, or group.;
+#X text 11 20 draw an array of svg shapes;
+#N canvas 731 197 450 323 draw-array-help-element 1;
+#X obj 68 15 struct draw-array-help-element float y;
+#X obj 149 89 draw circle 5;
+#X restore 117 104 pd draw-array-help-element;
+#X obj 117 64 struct draw-array-help-struct float x float y array a
+draw-array-help-element;
+#X scalar draw-array-help-struct 121 134 \; 0 \; 20 \; 25 \; 17 \;
+8 \; 29 \; 30 \; 12 \; 14 \; 9 \; \;;
+#X obj 118 271 draw array a 50 50;
+#X floatatom 118 211 5 0 0 0 - - -, f 5;
+#X msg 118 237 viewBox \$1 0 50 50;
+#X text 261 245 Note: still a work-in-progress;
+#X connect 27 0 28 0;
+#X connect 28 0 26 0;
diff --git a/pd/doc/5.reference/drawimage-help.pd b/pd/doc/5.reference/drawimage-help.pd
new file mode 100644
index 0000000000000000000000000000000000000000..f0e309a72368d72127fb1921d6e911314662903c
--- /dev/null
+++ b/pd/doc/5.reference/drawimage-help.pd
@@ -0,0 +1,62 @@
+#N struct draw-image-help-struct float x float y;
+#N canvas 238 53 555 619 10;
+#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
+14 -228856 -66577 0;
+#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header draw\ image 3 12
+0 18 -204280 -1 0;
+#X obj 0 329 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+-228856 -1 0;
+#N canvas 494 296 482 332 META 0;
+#X text 12 115 LIBRARY internal;
+#X text 12 25 LICENSE SIBSD;
+#X text 12 5 KEYWORDS control GUI data-structure;
+#X text 12 45 DESCRIPTION draw an svg shape to represent a scalar;
+#X text 12 65 INLET_0 float fill fill-opacity fill-rule stroke stroke-dasharray
+stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width
+rx ry;
+#X text 12 135 AUTHOR Jonathan Wilkes;
+#X text 13 155 HELP_PATCH_AUTHORS Jonathan Wilkes;
+#X restore 500 597 pd META;
+#X obj 0 396 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+13 -228856 -1 0;
+#X obj 0 433 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+0 13 -228856 -1 0;
+#X obj 0 533 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+0 13 -228856 -1 0;
+#X obj 78 337 cnv 17 3 50 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#N canvas 212 516 428 108 Related_objects 0;
+#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
+14 -204280 -1 0;
+#X obj 27 35 draw;
+#X text 7 1 [draw image] Related Objects;
+#X restore 101 597 pd Related_objects;
+#X text 99 405 float;
+#X obj 78 405 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#X text 169 405 - outputs the stored value as a float message.;
+#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
+;
+#X floatatom 80 168 5 0 0 0 - - -, f 5;
+#X msg 80 192 transform skewx \$1;
+#X text 98 336 float;
+#X text 81 450 1) symbol;
+#X text 11 20 draw an svg image or sequence of images;
+#X obj 117 64 struct draw-image-help-struct float x float y;
+#X obj 80 253 draw image;
+#X scalar draw-image-help-struct 324 189 \;;
+#X text 168 337 - for [draw sprite] \, the index of the image to display.
+This has no effect for [draw image];
+#X text 171 450 - path to the image to display. Or for [draw sprite]
+the path which contains a sequence of images;
+#X text 81 485 2) float;
+#X text 171 485 - x origin for the image;
+#X text 81 505 3) float;
+#X text 171 505 - y origin for the image;
+#X obj 162 541 pddp/pddplink drawarray-help.pd -text draw array;
+#X text 91 543 See also:;
+#X obj 162 561 pddp/pddplink drawimage-help.pd -text draw image;
+#X obj 232 541 pddp/pddplink drawsprite-help.pd -text draw sprite;
+#X obj 232 561 pddp/pddplink ./drawsvg-help.pd -text draw svg;
+#X connect 13 0 14 0;
+#X connect 14 0 19 0;
diff --git a/pd/doc/5.reference/drawsprite-help.pd b/pd/doc/5.reference/drawsprite-help.pd
new file mode 100644
index 0000000000000000000000000000000000000000..08569c970da5e8e64b16c6ce84ee99a4196e83ef
--- /dev/null
+++ b/pd/doc/5.reference/drawsprite-help.pd
@@ -0,0 +1,139 @@
+#N struct draw-sprite-help float x float y;
+#N canvas 255 53 555 619 10;
+#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
+14 -228856 -66577 0;
+#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header draw\ sprite 3 12
+0 18 -204280 -1 0;
+#X obj 0 349 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+-228856 -1 0;
+#N canvas 466 266 482 355 META 0;
+#X text 12 115 LIBRARY internal;
+#X text 12 25 LICENSE SIBSD;
+#X text 12 5 KEYWORDS control GUI data-structure;
+#X text 12 45 DESCRIPTION draw an svg shape to represent a scalar;
+#X text 12 65 INLET_0 float fill fill-opacity fill-rule stroke stroke-dasharray
+stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width
+rx ry;
+#X text 12 135 AUTHOR Jonathan Wilkes;
+#X text 13 155 HELP_PATCH_AUTHORS Jonathan Wilkes;
+#X restore 500 597 pd META;
+#X obj 0 446 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+13 -228856 -1 0;
+#X obj 0 483 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+0 13 -228856 -1 0;
+#X obj 0 553 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+0 13 -228856 -1 0;
+#X obj 78 357 cnv 17 3 80 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#N canvas 212 516 428 108 Related_objects 0;
+#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
+14 -204280 -1 0;
+#X obj 17 35 draw image;
+#X obj 92 36 draw;
+#X text 7 1 [draw sprite] Related Objects;
+#X restore 101 597 pd Related_objects;
+#X obj 78 455 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
+;
+#X obj 115 56 struct draw-sprite-help float x float y;
+#X obj 293 81 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X obj 293 101 metro 72;
+#X obj 293 123 f;
+#X obj 333 123 + 1;
+#X obj 293 150 % 22;
+#X msg 293 235 index \$1;
+#N canvas 681 181 450 323 earth 0;
+#X obj 62 8 loadbang;
+#X msg 62 30 fill white \, stroke black \, stroke-width 3.98213;
+#X obj 62 52 draw path m 125.441 64.4134 a 60.2251 60.2251 0 1 1 -120.45
+0 a 60.2251 60.2251 0 1 1 120.45 0 z;
+#X obj 62 120 draw path m 36.6489 11.9887 c 4.57718 -3.98317 10.7935
+-5.0472 16.5767 -5.81134 c 3.18504 -1.70824 6.31674 0.661606 7.88578
+1.63077 c 1.39674 -2.8954 6.10067 1.22814 5.64238 -2.96455 c 2.59712
+-0.816215 4.03226 4.04543 6.8129 1.44597 c 3.1605 3.03694 -5.06567
+3.97271 -3.48434 6.17877 c 4.74075 -0.533508 7.14687 3.59104 10.9833
+5.05019 c -1.77232 -2.9487 -0.236351 -3.0666 -1.08089 -4.33422 c -1.70082
+-2.55124 -4.04772 -4.10695 0.967781 -2.90709 c 5.74748 3.20745 3.22719
+-1.26601 10.1905 4.28407 c 2.07054 -0.295027 2.88148 1.63521 2.82493
+2.80785 c -0.044815 0.936308 -0.633278 1.74884 -2.82493 0.431078 c
+-2.9471 2.10148 -3.04153 3.66146 -0.571915 1.14331 c 2.42639 0.024004
+2.46533 4.76796 5.33081 2.10042 c -0.071487 0.805065 -0.488159 1.82406
+-1.03928 2.06894 c -0.724503 0.322243 -1.68161 -0.250748 -2.39651 0.004803
+c -0.856285 0.306236 -1.03821 0.967253 -1.3231 2.68515 c -1.97984 0.488155
+-2.80679 1.55197 -2.74276 2.97484 c 0.102966 2.28127 1.64961 5.16915
+-1.95638 5.70373 c -4.16509 2.05453 4.11335 9.57379 0.125908 9.42281
+c -2.49682 -2.52563 -3.15836 -6.4101 -7.54754 -5.47752 c -1.92757 -0.336651
+-2.34103 2.93749 -5.05071 0.927769 c -5.55862 -0.333981 -6.88439 6.72913
+-4.55029 10.4562 c 1.51356 4.85706 7.67611 1.85447 8.48598 -1.4058
+c 7.43977 -1.17158 -2.99565 9.21956 5.04272 7.69373 c 5.22891 -0.359581
+0.569252 8.99388 7.2285 7.77055 c 3.93409 0.47855 8.10132 -7.83297
+11.7628 -2.46001 c 2.07641 -0.521767 4.44412 -1.20785 6.30073 -0.417732
+c 2.1479 0.914429 3.74896 3.43952 5.6221 4.62178 c 6.18336 -0.486023
+1.35885 7.8399 7.38376 7.29892 c 6.32047 -0.660477 1.77445 6.23991
+1.39778 9.54607 c -1.61172 4.81811 -3.62944 9.54338 -6.3514 13.844
+c -4.9643 2.33623 -7.06739 8.11413 -11.0516 11.623 c -4.88587 1.42712
+-8.13493 5.76881 -12.4457 8.1584 c -2.41359 2.19859 -7.34267 5.98543
+-9.65596 5.20863 c 2.23113 -4.06532 5.00697 -7.37788 7.69212 -11.2389
+c 2.08067 -4.69486 9.2889 -10.9513 6.09533 -15.8004 c -5.09981 -2.38317
+-5.30521 -9.2857 -8.58522 -13.4289 c 2.15698 -2.10842 -0.325966 -6.02116
+3.56171 -7.95834 c 1.90089 -1.61653 2.04974 -9.53913 -1.85608 -7.05512
+c -5.60664 -1.25427 -8.09438 -7.61102 -13.9587 -7.99727 c -3.67107
+-3.2544 -9.10806 -1.65655 -13.5826 -4.74289 c -4.299 -1.90676 -3.89513
+-8.03836 -7.90979 -10.5848 c -0.979523 -1.29376 -3.42725 -6.86838 -4.23018
+-3.91649 c 0.296097 2.15163 6.10867 10.2364 1.52423 5.52554 c -3.09061
+-4.2766 -4.67299 -9.39934 -7.4307 -13.9096 c -2.43407 -4.04186 2.31276
+-7.36615 1.52424 -11.0511 c 2.66701 -0.489227 -2.42106 -4.53055 -1.79099
+-7.16448 c -1.94709 -2.7337 -2.29984 0.273157 -4.68734 -0.457218 c
+-0.184116 2.0284 -1.78496 1.17265 -2.85816 0.476421 z;
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
+#X restore 335 177 draw g earth;
+#X scalar draw-sprite-help 87 105 \;;
+#X floatatom 293 175 5 0 0 0 - - -, f 5;
+#N canvas 0 0 450 300 rotate 0;
+#X obj 98 21 inlet;
+#X obj 98 182 outlet;
+#X obj 108 115 loadbang;
+#X msg 98 158 transform translate -30 85 rotate \$1 65 63;
+#X msg 98 46 360 \$1;
+#X obj 98 68 -;
+#X obj 98 90 * 2.5;
+#X msg 108 137 0;
+#X connect 0 0 4 0;
+#X connect 2 0 7 0;
+#X connect 3 0 1 0;
+#X connect 4 0 5 0;
+#X connect 5 0 6 0;
+#X connect 6 0 3 0;
+#X connect 7 0 3 0;
+#X restore 335 150 pd rotate;
+#X text 98 356 index;
+#X text 168 357 - index of the image to display from the loaded image
+sequence. Indices are zero-based.;
+#X text 98 396 [draw] also takes a number of messages. These are svg
+attributes that define how the object is drawn. See the [draw] object
+for more information about them.;
+#X text 99 455 -;
+#X text 169 455 - -;
+#X text 81 500 1) directory;
+#X text 91 570 Images may be: png \, jpg \, gif \, or svg;
+#X text 171 500 - directory in which a sequence of images may be found.
+Purr Data will load the images files found there in alphabetical order.
+Relative paths are relative to the patch.;
+#X obj 179 327 pddp/pddplink http://millionthvector.blogspot.com/p/free-sprites_12.html
+;
+#X text 237 309 Sprite from;
+#X obj 293 259 draw sprite ./drawsprite_images;
+#X text 11 20 draw a sprite from an image sequence;
+#X connect 12 0 13 0;
+#X connect 13 0 14 0;
+#X connect 14 0 15 0;
+#X connect 14 0 16 0;
+#X connect 14 0 21 0;
+#X connect 15 0 14 1;
+#X connect 16 0 20 0;
+#X connect 17 0 32 0;
+#X connect 20 0 17 0;
+#X connect 21 0 18 0;
diff --git a/pd/doc/5.reference/drawsprite_images/longneck01.png b/pd/doc/5.reference/drawsprite_images/longneck01.png
new file mode 100644
index 0000000000000000000000000000000000000000..c97e62d0047e02949c546856ba42fa88817bc09c
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck01.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck02.png b/pd/doc/5.reference/drawsprite_images/longneck02.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0fea79b0247be652881ff221655a483f7a1023f
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck02.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck03.png b/pd/doc/5.reference/drawsprite_images/longneck03.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a636593f6bd78eb875bbff4ef564320dc59e64b
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck03.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck04.png b/pd/doc/5.reference/drawsprite_images/longneck04.png
new file mode 100644
index 0000000000000000000000000000000000000000..a54afa16392ec9eeb989621240af61f560ad14aa
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck04.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck05.png b/pd/doc/5.reference/drawsprite_images/longneck05.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f0b1dd8c6cfa51059fcab7c98cb46973cd3d31a
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck05.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck06.png b/pd/doc/5.reference/drawsprite_images/longneck06.png
new file mode 100644
index 0000000000000000000000000000000000000000..d480183b9239304f5fd94c5f01feb5ed2e0cd13a
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck06.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck07.png b/pd/doc/5.reference/drawsprite_images/longneck07.png
new file mode 100644
index 0000000000000000000000000000000000000000..0546715d7f1fb44f1d2037da8cd603769815f1b9
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck07.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck08.png b/pd/doc/5.reference/drawsprite_images/longneck08.png
new file mode 100644
index 0000000000000000000000000000000000000000..921004eb1799a70bcd30410be03d48fd109a471d
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck08.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck09.png b/pd/doc/5.reference/drawsprite_images/longneck09.png
new file mode 100644
index 0000000000000000000000000000000000000000..289de0feafb178cd7c675c41da5c37ecbddea646
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck09.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck10.png b/pd/doc/5.reference/drawsprite_images/longneck10.png
new file mode 100644
index 0000000000000000000000000000000000000000..97ffe0c6acf97fda816b062b853d78e56b55d9d3
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck10.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck11.png b/pd/doc/5.reference/drawsprite_images/longneck11.png
new file mode 100644
index 0000000000000000000000000000000000000000..bb8fd71bb79005eab7df7ce9cf4562fa19d14848
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck11.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck12.png b/pd/doc/5.reference/drawsprite_images/longneck12.png
new file mode 100644
index 0000000000000000000000000000000000000000..aca29148b26a588e8b9b4160fffb895ad20df7ae
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck12.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck13.png b/pd/doc/5.reference/drawsprite_images/longneck13.png
new file mode 100644
index 0000000000000000000000000000000000000000..50bfdd8a761ea0837f2d765717aa2b8a47d78148
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck13.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck14.png b/pd/doc/5.reference/drawsprite_images/longneck14.png
new file mode 100644
index 0000000000000000000000000000000000000000..98dc9229b06dc091e0b4a8103e0a56716d0c4df6
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck14.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck15.png b/pd/doc/5.reference/drawsprite_images/longneck15.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b658c106b178555b1a4d9e81353606cba9aafea
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck15.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck16.png b/pd/doc/5.reference/drawsprite_images/longneck16.png
new file mode 100644
index 0000000000000000000000000000000000000000..071909bb8d838dba0face63c49c749479c6be4fc
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck16.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck17.png b/pd/doc/5.reference/drawsprite_images/longneck17.png
new file mode 100644
index 0000000000000000000000000000000000000000..14e46774acd10a4dd14f81d09de04d33aeeb7219
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck17.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck18.png b/pd/doc/5.reference/drawsprite_images/longneck18.png
new file mode 100644
index 0000000000000000000000000000000000000000..2977fe30266c494af4e72ba8b315c7c21fc9f215
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck18.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck19.png b/pd/doc/5.reference/drawsprite_images/longneck19.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c75d7a038c843ee1722e1301be94c5aea17142d
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck19.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck20.png b/pd/doc/5.reference/drawsprite_images/longneck20.png
new file mode 100644
index 0000000000000000000000000000000000000000..024eb0fd9acd98bd257d2a24b5619a54b436d91a
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck20.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck21.png b/pd/doc/5.reference/drawsprite_images/longneck21.png
new file mode 100644
index 0000000000000000000000000000000000000000..5810c8723e39de66790e44631aff94b05bce50e6
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck21.png differ
diff --git a/pd/doc/5.reference/drawsprite_images/longneck22.png b/pd/doc/5.reference/drawsprite_images/longneck22.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef61f97e3fe96d11b7abc9151bd344191199eb1e
Binary files /dev/null and b/pd/doc/5.reference/drawsprite_images/longneck22.png differ
diff --git a/pd/doc/5.reference/drawsvg-help.pd b/pd/doc/5.reference/drawsvg-help.pd
new file mode 100644
index 0000000000000000000000000000000000000000..ed2ba4d654a10c37151cd250320db1c0adf83c6f
--- /dev/null
+++ b/pd/doc/5.reference/drawsvg-help.pd
@@ -0,0 +1,171 @@
+#N struct draw-svg-help-struct float x float y;
+#N canvas 157 53 555 619 10;
+#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
+14 -228856 -66577 0;
+#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header draw\ svg 3 12 0
+18 -204280 -1 0;
+#X obj 0 347 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
+-228856 -1 0;
+#N canvas 494 296 482 332 META 0;
+#X text 12 115 LIBRARY internal;
+#X text 12 25 LICENSE SIBSD;
+#X text 12 5 KEYWORDS control GUI data-structure;
+#X text 12 45 DESCRIPTION draw an svg shape to represent a scalar;
+#X text 12 65 INLET_0 float fill fill-opacity fill-rule stroke stroke-dasharray
+stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width
+rx ry;
+#X text 12 135 AUTHOR Jonathan Wilkes;
+#X text 13 155 HELP_PATCH_AUTHORS Jonathan Wilkes;
+#X restore 500 597 pd META;
+#X obj 0 444 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
+13 -228856 -1 0;
+#X obj 0 481 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
+0 13 -228856 -1 0;
+#X obj 0 542 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
+0 13 -228856 -1 0;
+#X obj 78 355 cnv 17 3 80 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#N canvas 212 516 428 108 Related_objects 0;
+#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
+14 -204280 -1 0;
+#X obj 27 35 draw;
+#X text 7 1 [draw svg] Related Objects;
+#X restore 101 597 pd Related_objects;
+#X obj 78 453 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
+-162280 0;
+#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
+;
+#X text 98 394 [draw] also takes a number of messages. These are svg
+attributes that define how the object is drawn. See the subpatch above
+for a full list.;
+#X obj 172 552 pddp/pddplink drawarray-help.pd -text draw array;
+#X text 101 554 See also:;
+#X obj 172 572 pddp/pddplink drawimage-help.pd -text draw image;
+#X obj 242 552 pddp/pddplink drawsprite-help.pd -text draw sprite;
+#X text 11 20 draw shapes inside an svg container;
+#X obj 146 52 struct draw-svg-help-struct float x float y;
+#X scalar draw-svg-help-struct 207 120 \;;
+#X obj 34 137 r \$0-drag;
+#X obj 34 159 route drag;
+#N canvas 0 0 450 300 pan 0;
+#X obj 50 51 unpack p 0 0;
+#X floatatom 82 75 5 0 0 0 - - -, f 5;
+#X floatatom 123 72 5 0 0 0 - - -, f 5;
+#X obj 47 20 inlet;
+#X obj 71 122 * -1;
+#X obj 128 120 * -1;
+#X obj 71 154 +;
+#X obj 128 152 +;
+#X obj 71 176 t a;
+#X obj 128 174 t a;
+#X obj 71 198 outlet;
+#X obj 128 196 outlet;
+#X connect 0 1 1 0;
+#X connect 0 1 4 0;
+#X connect 0 2 2 0;
+#X connect 0 2 5 0;
+#X connect 3 0 0 0;
+#X connect 4 0 6 0;
+#X connect 5 0 7 0;
+#X connect 6 0 8 0;
+#X connect 7 0 9 0;
+#X connect 8 0 6 1;
+#X connect 8 0 10 0;
+#X connect 9 0 7 1;
+#X connect 9 0 11 0;
+#X restore 34 181 pd pan;
+#X obj 33 211 pack;
+#X text 98 354 viewBox;
+#X text 168 355 - Four floats to define the viewport of the container:
+xOrigin yOrigin width height. See svg spec for more details;
+#X text 99 453 anything;
+#X text 169 453 - messages in response to any events set for the object
+;
+#X text 81 498 1) float;
+#X text 171 498 - initial width of the container;
+#X text 81 515 2) float;
+#X text 171 515 - initial height of the container;
+#N canvas 492 53 686 639 (subpatch) 0;
+#X obj 41 52 draw path m 5 194.088 l 666.819 0 l 0 -36.9314 l -3.12158
+0 l 0 3.03125 l -5.96875 0 l 0 -1.5 l -7.40625 0 l 0 5.40625 l -5.65625
+0 l 0 1.6875 l -3.125 0 l 0 -1.65625 l -5.40625 0 l -6.84375 -6.84375
+l -1.84375 3.21875 l -2.5625 0 l 0 -19.1562 c 1.11498 -0.60865 1.90625
+-1.76523 1.90625 -3.125 c 0 -1.35977 -0.79127 -2.51635 -1.90625 -3.125
+l 0 -13.4375 l 2.03125 0 l 0 -2.3125 l -2.6938 0 l -0.99404 -12.4839
+l -0.90632 12.4839 l -3.62459 0 l 0 1.84375 l 2.51562 0 l 0 1.79466
+l -1.23437 0 l 0 1.99861 l 1.5625 0 l 0 10.0817 c -1.14211 0.60033
+-1.9375 1.77639 -1.9375 3.15625 c 0 1.37986 0.79539 2.55592 1.9375
+3.15625 l 0 19.625 l -2.90625 0 l 0 -5.59375 l -3.875 -3.90625 l -6.65625
+3.84375 l 0 -2.5 l -3.53125 0.96875 l 0 7.4375 l -2.59375 0 l 0 -3.8125
+l -17.5938 0 l 0 4 l -5.03125 0 l 0 -3.75 l -11.875 0 l 0 3.3125 l
+-3.78125 0 l -0.78125 -26.4688 l -0.78125 25.1875 l -2.3125 0 l 0 -3.96875
+l -2.9375 0 l 0 3.5 l -3.5 0 l 0 -2.1875 l -5.21875 0 l 0 5.1875 l
+-7.8125 0 l 0 -2.53125 l -7.53125 0 l 0.25 -20.8438 l -1.28125 21.3438
+l -4.09375 0 l 0 -6.3125 l -3.1875 0 l -0.9375 3.5 l -3.875 0 l 0 4.3125
+l -2.3125 0 l 0 -6.34375 l -2.03125 -0.53125 l 0 4.875 l -1.28125 0
+l 0 2.40625 l -1.59375 0 l 0 3.5 l -4.59375 0 l 0 -23.125 l -11.6875
+0 l 0 -3.15625 l -3.46875 0 l 0 3.15625 l -3.875 0 l 0 8.71875 l -4.09375
+0 l 0 5.15625 l -3.09375 0 l 0 4.375 l -2.125 0 l 0 -3.375 l -1.71875
+0 l 0 -5.40625 l -6.59375 0 l 0 2.59375 l -8.59375 0 l 0 3.34375 l
+-9.75 0 l 0 -14.7188 l -3.90625 0 l 0 -1.71875 l -3.5625 0 l 0 2.3125
+l -2.0625 0 l 0 -2.59375 l -6.15625 0 l 0 6.96875 l -3.125 0 l 0 5.1875
+l -1.5625 0 l 0 -9 l -1.5 0 l 0 -2.15625 l -5.75 0 l -0.65625 -2.53125
+l -12.0938 0 l 0 5.15625 l -2.0625 0 l 0 6.6875 l -4.375 0 l -1.84375
+-3.09375 l 0 -77.75 l -1.46875 0 l 0 -1.96875 l 3.15625 0 l 0 -5.1875
+l 1.6875 0 l 0 -3.84375 l -1.6875 0 l 0 -8.28125 l -1.375 0 l 0 -3.5
+l 6.03125 0 l 0 -2.5625 l -2.4375 0 l 0 -5.0625 l 2.1875 0 l 0 -2.1875
+l -4.03125 0 l 0 -1.21875 l -2.78125 0 l 0 -3.125 l 1.71875 0 l 0 -1.9375
+l -4.28125 0 L 405.166 5 l -1.40625 26.7812 l -5.34375 0 l 0 1.9375
+l 1.96875 0 l 0 3.4375 c -2.95107 0.40253 -5.2699 1.439 -6.375 3.6875
+l 0 2.96875 l 2.03125 0 l 0 2.78125 l -1.40625 0 l 0 1.78125 l 5.09375
+0 l 0 3 l -2.125 0 l 0 8.28125 l -1.9375 0 l 0 3.84375 l 1.9375 0 l
+0 4.6875 l 3.53125 0 l 0 2.46875 l -2.0625 0 l 0 78.25 l -12.4062 0
+l 0 11.8438 l -5.65625 0 l 0 -22.125 l -5.40625 0 l 0 -1.53125 l -6.375
+0 l -0.46875 -9.625 l -0.4375 9.625 l -8.57193 0 l 0 3.09375 l -3.0625
+0 l 0 5.375 l -16.6191 0 l 0 5.65625 l -5.125 0 l 0 -2.90625 l -0.78125
+-3 l -5.65625 0 l 0 2.84375 l -1.37218 0 l 0 15.3442 l -3.41612 0 l
+0 -18.438 l -8.0867 0 l 0 -3.59375 l -4 0 l 0 3.78125 l -1.90625 1.09375
+l 0 5.65625 l -33.2439 0 l 0 1.3125 l -1.8125 0 l 0 -4.40625 l -4.875
+0 l 0 -1.03125 l -3.875 0 l 0 -2.5625 l -4.375 0 l 0 6.6875 l -12.1514
+0 l 0 -2.5625 l -3.84375 0 l 0 -3.34375 l -4.25 0 l 0 10.4062 l -3.21875
+-3.21875 l 0 -25.9688 l -6.15625 0 l 0 -6.15625 l -5.15625 0 l 0 13.375
+l -6.99996 0 l 0 28.0945 l -6.09379 0 l 0 -8.06316 l -2.90625 -3.28125
+l -3.53125 3.28125 l 0 -8.8125 l -5.28125 0 l 0 -2.875 l -5.59375 0
+l 0 3.96875 l -3.5625 0 l 0 4.6875 l -1.5 0 l 0 -1.84375 l -10.7927
+0 l 0 3.125 l -17.4334 0 l 0 -3.34375 l -3.96875 0 l 0 -2.34375 l -6.9375
+0 l 0 -3.34375 l -3.84375 0 l -0.90625 -32.4375 c 1.7461 -0.54309 3.03125
+-2.16894 3.03125 -4.09375 c 0 -2.01445 -1.409 -3.66423 -3.28125 -4.125
+l -0.84375 -30.25 l -1.375 30.2812 c -1.77984 0.52232 -3.09375 2.14493
+-3.09375 4.09375 c 0 1.81116 1.13148 3.37093 2.71875 4 l -1.96875 43.7812
+l -4.46905 4.75707 l 0 3.18537 l -6.03095 -4.03614 l 0 -11.5625 l -19.25
+0 l 0 6.0625 l -2.59375 0 l 0 -78.5 l -32.9688 0 l 0 74.2188 l -4.53125
+0 l 0 2.5625 l -4.03125 0 l 0 2.34375 l -3.46875 0 l 0 -4.375 l -2.3125
+0 l 0 -5.40625 l -3.34375 0 l 0 -6.6875 l -6.0746 0 l 0 22.754 l -4.59819
+0 l 0 -1.83383 l -8.10846 0.0134 l 0 1.97273 l -4.875 0 l 0 8.96875
+l -4.875 0 l 0 -8.96878 l -4.15862 0 l 0 -3.01004 l -2.34138 0 l 0
+3.01004 l -11.75 0 l 0 7.43748 l -2.3125 0 l 0 3.34375 l -5.50958 0
+z;
+#X obj 41 9 loadbang;
+#X msg 41 31 drag 1;
+#X obj 41 981 s \$0-drag;
+#X text 135 6 Skyline from:;
+#X obj 135 26 pddp/pddplink https://upload.wikimedia.org/wikipedia/commons/1/13/JohannesburgArtisticSilhouette.svg
+;
+#X connect 0 0 3 0;
+#X connect 1 0 2 0;
+#X connect 2 0 0 0;
+#X restore 33 285 draw svg 300 200;
+#X msg 33 233 viewBox \$1 \$2 300 200;
+#X obj 34 52 loadbang;
+#X msg 34 84 fill none \, stroke-width 2 \, stroke black \, stroke-dasharray
+5 5, f 61;
+#X obj 34 107 draw rect 300 200;
+#X text 270 322 Click to drag the skyline;
+#X connect 19 0 20 0;
+#X connect 20 0 21 0;
+#X connect 21 0 22 0;
+#X connect 21 1 22 1;
+#X connect 22 0 32 0;
+#X connect 32 0 31 0;
+#X connect 33 0 34 0;
+#X connect 34 0 35 0;
diff --git a/pd/nw/css/default.css b/pd/nw/css/default.css
index c40f4e37dea875f3f372f32afbbe0b921f333845..4df1ccc685a781c50eff018628c1243e7bfdb85e 100644
--- a/pd/nw/css/default.css
+++ b/pd/nw/css/default.css
@@ -381,7 +381,7 @@ text {
     stroke: #ccc;
 }
 
-/* A little hack for special canvas of [cnv].
+/* A little hack for special case of [cnv].
    All other iemguis have a black border, but
    [cnv] sets its selection rectangle to the
    user-supplied fill color when the object
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index b3f2d9f9567c386437a786655ef542726ec02b10..b849fd4144e0a13fb3fb0f595da7619e2e3f6234 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -3264,9 +3264,9 @@ function gui_scalar_draw_select_rect(cid, tag, state, x1, y1, x2, y2, basex, bas
     }
 }
 
-function gui_scalar_draw_group(cid, tag, parent_tag, attr_array) {
+function gui_scalar_draw_group(cid, tag, parent_tag, type, attr_array) {
     var parent_elem,
-        g;
+        group;
     if (!patchwin[cid]) {
         return;
     }
@@ -3275,9 +3275,9 @@ function gui_scalar_draw_group(cid, tag, parent_tag, attr_array) {
         attr_array = [];
     }
     attr_array.push("id", tag);
-    g = create_item(cid, "g", attr_array);
-    parent_elem.appendChild(g);
-    return g;
+    group = create_item(cid, type, attr_array);
+    parent_elem.appendChild(group);
+    return group;
 }
 
 function gui_scalar_configure_gobj(cid, tag, isselected, t1, t2, t3, t4, t5, t6) {
@@ -3383,6 +3383,19 @@ function gui_draw_configure(cid, tag, attr, val) {
     configure_item(item, obj);
 }
 
+// Special case for viewBox which, in addition to its inexplicably inconsistent
+// camelcasing also has no "none" value in the spec. This requires us to create
+// a special case to remove the attribute if the user wants to get back to
+// the default behavior.
+function gui_draw_viewbox(cid, tag, attr, val) {
+    // Value will be an empty array if the user provided no values
+    if (val.length) {
+        gui_draw_configure(cid, tag, attr, val)
+    } else {
+        get_item(cid, tag).removeAttribute("viewBox");
+    }
+}
+
 // Configure multiple attr/val pairs (this should be merged with gui_draw_configure at some point
 function gui_draw_configure_all(cid, tag, attr_array) {
     var item = get_item(cid, tag);
@@ -3507,55 +3520,58 @@ function gui_drawimage_new(obj_tag, file_path, canvasdir, flags) {
     var drawsprite = 1,
         drawimage_data = [], // array for base64 image data
         image_seq,
-        i,
+        count = 0,
         matchchar = "*",
         files,
         ext,
+        img_types = [".gif", ".jpeg", ".jpg", ".png", ".svg"],
         img; // dummy image to measure width and height
     image_seq = flags & drawsprite;
-    if (!path.isAbsolute(file_path)) {
-        file_path = path.join(canvasdir, file_path);
+    if (file_path !== "") {
+        if(!path.isAbsolute(file_path)) {
+            file_path = path.join(canvasdir, file_path);
+        }
+        file_path = path.normalize(file_path);
     }
-    file_path = path.normalize(file_path);
-    if (fs.existsSync(file_path) && fs.lstatSync(file_path).isDirectory()) {
+    if (file_path !== "" &&
+        fs.existsSync(file_path) &&
+        fs.lstatSync(file_path).isDirectory()) {
         files = fs.readdirSync(file_path)
                     .sort(); // Note that js's "sort" method doesn't do the
                              // "right thing" for numbers. For that we'd need
                              // to provide our own sorting function
         // todo: warn about image sequence with > 999
-        for (i = 0; i < files.length && i < 1000; i++) {
-            ext = path.extname(files[i]);
-
-        // todo: tolower()
-
-            if (ext === ".gif" ||
-                ext === ".jpg" ||
-                ext === ".png" ||
-                ext === ".jpeg" ||
-                ext === ".svg") {
-
+        files.forEach(function(file) {
+            ext = path.extname(file).toLowerCase();
+            if (img_types.indexOf(ext) != -1) {
                 // Now add an element to that array with the image data
                 drawimage_data.push({
                     type: ext === ".jpeg" ? "jpg" : ext.slice(1),
-                    data: fs.readFileSync(path.join(file_path, files[i]),"base64")
+                    data: fs.readFileSync(path.join(file_path, file),"base64")
                 });
+                count++;
             }
-        }
-    } else {
-        i = 0;
+        });
     }
-    //post("no of files: " + i);
+    post("no of files: " + count);
 
-    if (i > 0) {
-        img = new pd_window.Image(); // create an image in the pd_window context
-        img.onload = function() {
-            pdsend(obj_tag, "size", this.width, this.height);
-        };
-        img.src = "data:image/" + drawimage_data[0].type +
-            ";base64," + drawimage_data[0].data;
-    } else {
-        post("drawimage: warning: no images loaded");
+    if (count === 0) {
+        // set a default image
+        drawimage_data.push({
+            type: "png",
+            data: get_default_png_data()
+        });
+        if (file_path !== "") {
+            post("draw image: error: couldn't load image");
+        }
+        post("draw image: warning: no image loaded. Using default png");
     }
+    img = new pd_window.Image(); // create an image in the pd_window context
+    img.onload = function() {
+        pdsend(obj_tag, "size", this.width, this.height);
+    };
+    img.src = "data:image/" + drawimage_data[0].type +
+        ";base64," + drawimage_data[0].data;
     pd_cache.set(obj_tag, drawimage_data); // add the data to container
 }
 
@@ -3648,18 +3664,23 @@ function gui_drawimage_index(cid, obj, data, index) {
     configure_item(image, { visibility: "visible" });
 }
 
+// Default png image data
+function get_default_png_data() {
+    return ["iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAb1BMVEWBgYHX19",
+            "f///8vLy/8/Pzx8PH3+Pf19fXz8/Pu7u7l5eXj4+Pn5+fs7Oza2tr6+vnq6urh",
+            "4eHe3t7c3Nza2dr6+fro6Og1NTXr6+xYWFi1tbWjo6OWl5aLjItDQ0PPz8+/v7",
+            "+wsLCenZ5zc3NOTk4Rpd0DAAAAqElEQVQoz62L2Q6CMBBFhcFdCsomq+v/f6Mn",
+            "bdOSBn3ypNO5Nyez+kG0zN9NWZZK8RRbB/2XmMLSvSZp2mehTMVcLGIYbcWcLW",
+            "1/U4PIZCvmOCMSaWzEHGaMIq2NmJNn4ORuMybP6xxYD0SnE4NJDdc0fYv0LCJg",
+            "9g4RqV3BrJfB7Bzc+ILZOjC+YDYOjC+YKqsyHlOZAX5Msgwm1iRxgDYBSWjCm+",
+            "98AAfDEgD0K69gAAAAAElFTkSuQmCC"
+           ].join("");
+}
+
 function gui_load_default_image(dummy_cid, key) {
     pd_cache.set(key, {
         type: "png",
-        data: ["iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAb1BMVEWBgYHX19",
-               "f///8vLy/8/Pzx8PH3+Pf19fXz8/Pu7u7l5eXj4+Pn5+fs7Oza2tr6+vnq6urh",
-               "4eHe3t7c3Nza2dr6+fro6Og1NTXr6+xYWFi1tbWjo6OWl5aLjItDQ0PPz8+/v7",
-               "+wsLCenZ5zc3NOTk4Rpd0DAAAAqElEQVQoz62L2Q6CMBBFhcFdCsomq+v/f6Mn",
-               "bdOSBn3ypNO5Nyez+kG0zN9NWZZK8RRbB/2XmMLSvSZp2mehTMVcLGIYbcWcLW",
-               "1/U4PIZCvmOCMSaWzEHGaMIq2NmJNn4ORuMybP6xxYD0SnE4NJDdc0fYv0LCJg",
-               "9g4RqV3BrJfB7Bzc+ILZOjC+YDYOjC+YKqsyHlOZAX5Msgwm1iRxgDYBSWjCm+",
-               "98AAfDEgD0K69gAAAAAElFTkSuQmCC"
-              ].join("")
+        data: get_default_png_data()
     });
 }
 
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index e9c8f2d27c0b4a8a0e212cc07fcf79a6df5cb2ed..fb50bb6c253d2585e58295a619cf219b8b14c013 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -1101,9 +1101,9 @@ static void canvas_pop(t_canvas *x, t_floatarg fvis)
 extern void *svg_new(t_pd *x, t_symbol *s, int argc, t_atom *argv);
 extern t_pd *svg_header(t_pd *x);
 
-static void group_svginit(t_glist *gl)
+static void group_svginit(t_glist *gl, t_symbol *type, int argc, t_atom *argv)
 {
-    gl->gl_svg = (t_pd *)(svg_new((t_pd *)gl, gensym("g"), 0, 0));
+    gl->gl_svg = (t_pd *)(svg_new((t_pd *)gl, type, argc, argv));
     t_pd *proxy = svg_header(gl->gl_svg);
     inlet_new(&gl->gl_obj, proxy, 0, 0);
     outlet_new(&gl->gl_obj, &s_anything);
@@ -1116,10 +1116,14 @@ void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
     t_pd *z;
     int is_draw_command = 0;
     //fprintf(stderr,"canvas_restore %lx\n", x);
-    /* for [draw group] we add an inlet to the svg attr proxy */
-    if (argc > 2 && argv[2].a_w.w_symbol == gensym("draw"))
-    {
-        group_svginit(x);
+    /* for [draw g] and [draw svg] we add an inlet to the svg attr proxy */
+    if (atom_getsymbolarg(2, argc, argv) == gensym("draw"))
+    {
+        t_symbol *type = (atom_getsymbolarg(3, argc, argv) == gensym("svg")) ?
+            gensym("svg") : gensym("g");
+        group_svginit(x, type,
+            (type == gensym("svg") && argc > 4) ? argc-4 : 0,
+            (type == gensym("svg") && argc > 4) ? argv+4 : 0);
         is_draw_command = 1;
     }
     if (argc > 3 || (is_draw_command && argc > 4))
@@ -1352,12 +1356,18 @@ static void *subcanvas_new(t_symbol *s)
     return (x);
 }
 
-void *group_new(t_symbol *s)
+void *group_new(t_symbol *type, int argc, t_atom *argv)
 {
-    t_canvas *x = subcanvas_new(s);
-    group_svginit(x);
+    t_symbol *groupname;
+    if (type == gensym("g"))
+        groupname = atom_getsymbolarg(0, argc, argv);
+    else /* no name for inner svg */
+        groupname = &s_;
+    t_canvas *x = subcanvas_new(groupname);
+    group_svginit(x, type, argc, argv);
     return (x);
 }
+
 static void canvas_click(t_canvas *x,
     t_floatarg xpos, t_floatarg ypos,
         t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
@@ -1376,6 +1386,14 @@ void canvas_fattensub(t_canvas *x,
 
 static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av)
 {
+    /* special case for [draw g] where the 3rd arg is the receiver name */
+    if (x->gl_svg)
+    {
+        if (atom_getsymbolarg(0, ac, av) == gensym("g") && ac > 1)
+            ac--, av++;
+        else
+            ac = 0;
+    }
     if (ac && av->a_type == A_SYMBOL)
         canvas_rename(x, av->a_w.w_symbol, 0);
     else if (ac && av->a_type == A_DOLLSYM)
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index 38463cd52709b9ce0d476255e2280b1bf2a54636..7432aa57e6ae483010ce9c3025c5392b1caa029a 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -409,6 +409,11 @@ extern int array_joc;
 extern void template_notifyforscalar(t_template *template, t_glist *owner,
     t_scalar *sc, t_symbol *s, int argc, t_atom *argv);
 
+extern void scalar_getinnersvgrect(t_gobj *z, t_glist *owner, t_word *data,
+    t_template *template, t_float basex, t_float basey,
+    int *xp1, int *yp1, int *xp2, int *yp2);
+
+extern t_symbol *group_gettype(t_glist *glist);
 static void scalar_getgrouprect(t_glist *owner, t_glist *groupcanvas,
     t_word *data, t_template *template, int basex, int basey,
     int *x1, int *x2, int *y1, int *y2)
@@ -420,8 +425,12 @@ static void scalar_getgrouprect(t_glist *owner, t_glist *groupcanvas,
             ((t_canvas *)y)->gl_svg)
         {
             /* todo: accumulate basex and basey for correct offset */
-            scalar_getgrouprect(owner, (t_glist *)y, data, template,
-                basex, basey, x1, x2, y1, y2);
+            if (group_gettype((t_canvas *)y) == gensym("g"))
+                scalar_getgrouprect(owner, (t_glist *)y, data, template,
+                    basex, basey, x1, x2, y1, y2);
+            else /* inner svg */
+                scalar_getinnersvgrect(y, owner, data, template, basex, basey,
+                    x1, y1, x2, y2);
         }
         else
         {
@@ -876,8 +885,9 @@ static void scalar_groupvis(t_scalar *x, t_glist *owner, t_template *template,
         char parentbuf[MAXPDSTRING];
         sprintf(parentbuf, "dgroup%lx.%lx", (long unsigned int)parent,
             (long unsigned int)x->sc_vec);
-        gui_start_vmess("gui_scalar_draw_group", "xss",
-            glist_getcanvas(owner), tagbuf, parentbuf);
+        gui_start_vmess("gui_scalar_draw_group", "xsss",
+            glist_getcanvas(owner), tagbuf, parentbuf,
+            group_gettype(gl)->s_name);
         svg_grouptogui(gl, template, x->sc_vec);
         gui_end_vmess();
 
@@ -998,8 +1008,8 @@ static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
         sprintf(tagbuf, "scalar%lxgobj", (long unsigned int)x->sc_vec);
         sprintf(groupbuf, "dgroup%lx.%lx", (long unsigned int)templatecanvas,
             (long unsigned int)x->sc_vec);
-        gui_vmess("gui_scalar_draw_group", "xss",
-            glist_getcanvas(owner), groupbuf, tagbuf);
+        gui_vmess("gui_scalar_draw_group", "xsss",
+            glist_getcanvas(owner), groupbuf, tagbuf, "g");
         pd_bind(&x->sc_gobj.g_pd, gensym(buf));
     }
 
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index 866f6778722fafe1c29b3b6c8c5cc1489c2c02f0..dec213da843cdb96cfe5c3a719ee62848992c7e8 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -1102,6 +1102,8 @@ t_class *draw_class;
 
 t_class *drawimage_class;
 
+t_class *drawarray_class;
+
 t_class *svg_class;
 
 /* this is a wrapper around t_fielddesc-- it adds a flag for two reasons:
@@ -1187,6 +1189,7 @@ typedef struct _svg
     t_svg_attr x_width;
     t_svg_attr x_height;
     t_svg_attr x_vis;
+    t_svg_attr x_viewbox[4];
     t_fielddesc x_bbox; /* turn bbox calculation on or off */
     int x_pathrect_cache; /* 0 to recalc on next draw_getrect call
                              1 for cached
@@ -1214,18 +1217,23 @@ typedef struct _drawimage
 {
     t_object x_obj;
     t_fielddesc x_value; /* todo: rename to index */
-    t_fielddesc x_xloc;
-    t_fielddesc x_yloc;
     t_fielddesc x_vis;
     t_symbol *x_img;
     t_float x_w;
     t_float x_h;
     int x_flags;
-    int x_deleteme;
     t_canvas *x_canvas;
     t_pd *x_attr;
 } t_drawimage;
 
+typedef struct _drawarray
+{
+    t_object x_obj;
+    t_canvas *x_canvas;
+    t_fielddesc x_data;
+    t_pd *x_attr;
+} t_drawarray;
+
 extern t_outlet *obj_rightmost_outlet(t_object *x);
 
 void draw_notifyforscalar(t_object *x, t_glist *owner,
@@ -1287,9 +1295,9 @@ void svg_attr_setfloatarg(t_svg_attr *a, int argc, t_atom *argv)
     a->a_flag = 1;
 }
 
-void svg_attr_setfloat_const(t_svg_attr *a, float n)
+void svg_attr_setfloat_const(t_svg_attr *a, t_float n)
 {
-    fielddesc_setfloat_const(&a->a_attr, 0);
+    fielddesc_setfloat_const(&a->a_attr, n);
     a->a_flag = 0;
 }
 
@@ -1309,9 +1317,10 @@ void *svg_new(t_pd *parent, t_symbol *s, int argc, t_atom *argv)
     if (type == gensym("rect") ||
         type == gensym("circle") ||
         type == gensym("ellipse") ||
-        type == gensym("line"))
+        type == gensym("line") ||
+        type == gensym("svg"))
     {
-        if (type == gensym("rect"))
+        if (type == gensym("rect") || type == gensym("svg"))
         {
             if (argc) svg_attr_setfloatarg(&x->x_width, argc--, argv++);
             else svg_attr_setfloat_const(&x->x_width, 0);
@@ -1349,10 +1358,13 @@ void *svg_new(t_pd *parent, t_symbol *s, int argc, t_atom *argv)
         for (i = 0; i < ncmds; i++) x->x_nargs_per_cmd[i] = 0;
         x->x_nargs = argc - ncmds;
     }
-    else if (x->x_type == gensym("g"))
+    else if (type == gensym("g") || type == gensym("svg"))
     {
         x->x_nargs = 0;
         x->x_vec = 0;
+        /* Hack to get around the path parsing below, which should really
+           be split out... */
+        argc = 0;
     }
     else
     {
@@ -1407,6 +1419,8 @@ void *svg_new(t_pd *parent, t_symbol *s, int argc, t_atom *argv)
     x->x_strokemiterlimit.a_flag = 0;
     x->x_strokeopacity.a_flag = 0;
     x->x_strokewidth.a_flag = 0;
+    /* set the flag of the first array element... */
+    x->x_viewbox->a_flag = 0;
     x->x_x1 = 0;
     x->x_x2 = 0;
     x->x_y1 = 0;
@@ -1466,7 +1480,8 @@ static int symbol_isdrawtype(t_symbol *s)
         s == gensym("line")    || s == gensym("path")     ||
         s == gensym("polygon") || s == gensym("polyline") ||
         s == gensym("rect")    || s == gensym("image")    ||
-        s == gensym("sprite")  || s == gensym("g"))
+        s == gensym("sprite")  || s == gensym("g")        ||
+        s == gensym("svg")   || s == gensym("array"))
     {
         return 1;
     }
@@ -1475,7 +1490,8 @@ static int symbol_isdrawtype(t_symbol *s)
 }
 
 static void *drawimage_new(t_symbol *classsym, int argc, t_atom *argv);
-extern void *group_new(t_symbol *s);
+static void *drawarray_new(t_symbol *s, int argc, t_atom *argv);
+extern void *group_new(t_symbol *type, int argc, t_atom *argv);
 
 static void *draw_new(t_symbol *classsym, t_int argc, t_atom *argv)
 {
@@ -1484,19 +1500,14 @@ static void *draw_new(t_symbol *classsym, t_int argc, t_atom *argv)
         symbol_isdrawtype(argv[0].a_w.w_symbol))
     {
         type = atom_getsymbolarg(0, argc--, argv++);
-        /* sprite and image have their own widgetbehavior, so they
-        have their own class and new function */
+        /* sprite and image have their own widgetbehavior, and so does
+           array. They also have their own classes and constructors... */
         if (type == gensym("sprite") || type == gensym("image"))
             return (drawimage_new(type, argc, argv));
-        else if (type == gensym("g"))
-        {
-            t_symbol *group_name;
-            if (argc > 0 && argv->a_type == A_SYMBOL)
-                group_name = atom_getsymbolarg(0, argc, argv);
-            else group_name = &s_;
-            post("group name is %s", group_name->s_name);
-            return (group_new(group_name));
-        }
+        else if (type == gensym("array"))
+            return (drawarray_new(type, argc, argv));
+        else if (type == gensym("g") || type == gensym("svg"))
+            return (group_new(type, argc, argv));
     }
     else
     {
@@ -1537,7 +1548,7 @@ t_canvas *svg_parentcanvas(t_svg *x)
        use case, but that doesn't seem like a sensible
        interface in general. */
     t_canvas *ret = 0;
-    if (x->x_type == gensym("g"))
+    if (x->x_type == gensym("g") || x->x_type == gensym("svg"))
     {
         t_canvas *c = (t_canvas *)x->x_parent;
         if (c->gl_owner)
@@ -1551,6 +1562,11 @@ t_canvas *svg_parentcanvas(t_svg *x)
         t_drawimage *d = (t_drawimage *)x->x_parent;
         ret = d->x_canvas;
     }
+    else if (x->x_type == gensym("array"))
+    {
+        t_drawarray *d = (t_drawarray *)x->x_parent;
+        ret = d->x_canvas;
+    }
     else
     {
         t_draw *d = (t_draw *)x->x_parent;
@@ -1705,6 +1721,11 @@ t_svg_attr *svg_getattr(t_svg *x, t_symbol *s)
     return 0;
 }
 
+t_symbol *group_gettype(t_glist *glist)
+{
+    return ((t_svg *)glist->gl_svg)->x_type;
+}
+
 void svg_parsetransform(t_svg *x, t_template *template, t_word *data,
     t_float *mp1, t_float *mp2, t_float *mp3,
     t_float *mp4, t_float *mp5, t_float *mp6);
@@ -1723,7 +1744,7 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
     int in_array = (sc->sc_vec != data);
     //post("in_array is %d", in_array);
     char tag[MAXPDSTRING];
-    if (x->x_type == gensym("g"))
+    if (x->x_type == gensym("g") || x->x_type == gensym("svg"))
     {
         sprintf(tag, "%s%lx.%lx",
             (in_array ? "scelem" : "dgroup"),
@@ -1797,7 +1818,18 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
         t_float m1, m2, m3, m4, m5, m6;
         /* we'll probably get a different bbox now, so we will calculate a
            new one the next time we call draw_getrect for this draw command.
-           For groups we need to do it for all of the draw commands inside it.
+           For g we need to do it for all of the draw commands inside it.
+
+           For inner svgs, however, we can ignore the inner content since
+           it will never appear outside the width/height specified for the
+           container. (At least not for now, since we don't allow the user
+           to set an overflow style of "visible.") This allows us to get
+           a speedup when interacting with content inside an inner svg, as
+           Pd doesn't have to do any complex bbox calculations.
+
+           Unfortunately, the HTML5 getBBox() method calculates the boundaries
+           _without_ regard to clipping, so clipped content in inner svg will
+           still trigger scrollbars.
         */
         if (x->x_type == gensym("g"))
             svg_group_pathrect_cache(x, 0);
@@ -1930,6 +1962,25 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
         gui_vmess("gui_drawimage_index", "xxxi",
             glist_getcanvas(c), parent, data, drawimage_getindex(parent, template, data));
     }
+    else if (s == gensym("viewbox"))
+    {
+        gui_start_vmess("gui_draw_viewbox", "xss",
+            glist_getcanvas(c), tag, "viewBox");
+        gui_start_array();
+        if (x->x_viewbox->a_flag)
+        {
+            gui_f(fielddesc_getcoord(&x->x_viewbox[0].a_attr,
+                template, data, 0));
+            gui_f(fielddesc_getcoord(&x->x_viewbox[1].a_attr,
+                template, data, 0));
+            gui_f(fielddesc_getcoord(&x->x_viewbox[2].a_attr,
+                template, data, 0));
+            gui_f(fielddesc_getcoord(&x->x_viewbox[3].a_attr,
+                template, data, 0));
+        }
+        gui_end_array();
+        gui_end_vmess();
+    }
     else if (s == gensym("points"))
     {
         char tagbuf[MAXPDSTRING];
@@ -2175,6 +2226,22 @@ void svg_setattr(t_svg *x, t_symbol *s, t_int argc, t_atom *argv)
     }
 }
 
+/* Currently used just to update the arguments when the user changes
+   the arguments in an existing [draw svg] object. */
+void svg_update_args(t_svg *x, t_symbol *s, int argc, t_atom *argv)
+{
+post("made it to args");
+if (argc) post("first arg is %s", atom_getsymbolarg(0, argc, argv)->s_name);
+    /* "g" doesn't take any args, so check for "svg" arg */
+    if (atom_getsymbolarg(0, argc, argv) == gensym("svg"))
+    {
+        argc--, argv++;
+        if (argc) svg_setattr(x, gensym("width"), argc--, argv++), post("did width");
+        if (argc) svg_setattr(x, gensym("height"), argc--, argv++);
+        if (argc) svg_setattr(x, gensym("x"), argc--, argv++);
+        if (argc) svg_setattr(x, gensym("y"), argc--, argv++);
+    }
+}
 
 void svg_vis(t_svg *x, t_symbol *s, int argc, t_atom *argv)
 {
@@ -2577,6 +2644,29 @@ void svg_linepoints(t_svg *x, t_symbol *s, int argc, t_atom *argv)
     }
 }
 
+void svg_viewbox(t_svg *x, t_symbol *s, int argc, t_atom *argv)
+{
+    if (argc)
+    {
+        t_svg_attr *vbx = x->x_viewbox;
+        svg_attr_setfloatarg(vbx++, argc--, argv++);
+
+        if (argc) svg_attr_setfloatarg(vbx++, argc--, argv++);
+        else svg_attr_setfloat_const(vbx++, 0);
+
+        if (argc) svg_attr_setfloatarg(vbx++, argc--, argv++);
+        else svg_attr_setfloat_const(vbx++, 0);
+
+        if (argc) svg_attr_setfloatarg(vbx, argc, argv);
+        else svg_attr_setfloat_const(vbx, 0);
+    }
+    else
+    {
+        x->x_viewbox->a_flag = 0;
+    }
+    svg_update(x, gensym("viewbox"));
+}
+
 static int minv(t_float a[][3], t_float b[][3])
 {
     t_float tmp[3][3], determinant = 0;
@@ -3513,6 +3603,90 @@ static void svg_getpathrect(t_svg *x, t_glist *glist,
     *yp2 = (int)(finaly2 + basey);
 }
 
+static void svg_getrectrect(t_svg *x, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int *xp1, int *yp1, int *xp2, int *yp2)
+{
+    int width, height, xoff, yoff;
+    int x1, y1, x2, y2;
+    x1 = y1 = 0x7fffffff;
+    x2 = y2 = -0x7fffffff;
+
+    t_float mtx1[3][3] = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} };
+    t_float mtx2[3][3] = { {1, 0, 0}, {0, 1, 0}, {1, 0, 1} };
+    t_float m1, m2, m3, m4, m5, m6,
+            tx1, ty1, tx2, ty2, t5, t6;
+    if (!fielddesc_getfloat(&x->x_bbox, template, data, 0) ||
+        (x->x_vis.a_flag && !fielddesc_getfloat(&x->x_vis.a_attr,
+            template, data, 0)))
+    {
+        *xp1 = *yp1 = 0x7fffffff;
+        *xp2 = *yp2 = -0x7fffffff;
+        return;
+    }
+
+    svg_groupmtx(x, template, data, mtx1);
+    width = fielddesc_getcoord(&x->x_width.a_attr, template, data, 0);
+    height = fielddesc_getcoord(&x->x_height.a_attr, template, data, 0);
+    xoff = fielddesc_getcoord(&x->x_x.a_attr, template, data, 0);
+    yoff = fielddesc_getcoord(&x->x_y.a_attr, template, data, 0);
+ 
+    mset(mtx2, xoff, yoff, xoff + width, yoff + height, 0, 0);
+    mtx2[2][0] = 1; mtx2[2][1] = 1;
+    mmult(mtx1, mtx2, mtx2);
+    mget(mtx2, &tx1, &ty1, &tx2, &ty2, &t5, &t6);
+    if (tx1 < x1) x1 = tx1;
+    if (tx2 < x1) x1 = tx2;
+    if (ty1 < y1) y1 = ty1;
+    if (ty2 < y1) y1 = ty2;
+    if (tx1 > x2) x2 = tx1;
+    if (tx2 > x2) x2 = tx2;
+    if (ty1 > y2) y2 = ty1;
+    if (ty2 > y2) y2 = ty2;
+    mset(mtx2, xoff, yoff + height, xoff + width, yoff, 0, 0);
+    mtx2[2][0] = 1; mtx2[2][1] = 1;
+    mmult(mtx1, mtx2, mtx2);
+    mget(mtx2, &tx1, &ty1, &tx2, &ty2, &t5, &t6);
+    if (tx1 < x1) x1 = tx1;
+    if (tx2 < x1) x1 = tx2;
+    if (ty1 < y1) y1 = ty1;
+    if (ty2 < y1) y1 = ty2;
+    if (tx1 > x2) x2 = tx1;
+    if (tx2 > x2) x2 = tx2;
+    if (ty1 > y2) y2 = ty1;
+    if (ty2 > y2) y2 = ty2;
+    //x1 = glist_xtopixels(glist, basex + x1);
+    //x2 = glist_xtopixels(glist, basex + x2);
+    //y1 = glist_ytopixels(glist, basey + y1);
+    //y2 = glist_ytopixels(glist, basey + y2);
+
+    x1 = basex + x1;
+    x2 = basex + x2;
+    y1 = basey + y1;
+    y2 = basey + y2;
+
+    /* todo: put these up top */
+    if (!fielddesc_getfloat(&x->x_vis.a_attr, template, data, 0))
+    {
+        *xp1 = *yp1 = 0x7fffffff;
+        *xp2 = *yp2 = -0x7fffffff;
+        return;
+    }
+    *xp1 = x1;
+    *yp1 = y1;
+    *xp2 = x2;
+    *yp2 = y2;
+}
+
+void scalar_getinnersvgrect(t_gobj *z, t_glist *owner, t_word *data,
+    t_template *template, t_float basex, t_float basey,
+    int *xp1, int *yp1, int *xp2, int *yp2)
+{
+    t_canvas *c = (t_canvas *)z;
+    svg_getrectrect((t_svg *)c->gl_svg,
+        owner, data, template, basex, basey, xp1, yp1, xp2, yp2);
+}
+
 static void draw_getrect(t_gobj *z, t_glist *glist,
     t_word *data, t_template *template, t_float basex, t_float basey,
     int *xp1, int *yp1, int *xp2, int *yp2)
@@ -3811,13 +3985,13 @@ static void svg_togui(t_svg *x, t_template *template, t_word *data)
         }
     if (x->x_x.a_flag)
     {
-        gui_s(x->x_type == gensym("rect") ? "x" :
+        gui_s(x->x_type == gensym("rect") || x->x_type == gensym("svg") ? "x" :
             x->x_type == gensym("line") ? "x1" : "cx");
         gui_f(fielddesc_getcoord(&x->x_x.a_attr, template, data, 0));
     }
     if (x->x_y.a_flag)
     {
-        gui_s(x->x_type == gensym("rect") ? "y" :
+        gui_s(x->x_type == gensym("rect") || x->x_type == gensym("svg") ? "y" :
             x->x_type == gensym("line") ? "y1" : "cy");
         gui_f(fielddesc_getcoord(&x->x_y.a_attr, template, data, 0));
     }
@@ -3944,6 +4118,20 @@ static void svg_togui(t_svg *x, t_template *template, t_word *data)
         gui_f(fielddesc_getcoord(&x->x_ry.a_attr,
             template, data, 0));
     }
+    if (x->x_viewbox->a_flag)
+    {
+        gui_s("viewBox");
+        gui_start_array();
+        gui_f(fielddesc_getcoord(&x->x_viewbox[0].a_attr,
+            template, data, 0));
+        gui_f(fielddesc_getcoord(&x->x_viewbox[1].a_attr,
+            template, data, 0));
+        gui_f(fielddesc_getcoord(&x->x_viewbox[2].a_attr,
+            template, data, 0));
+        gui_f(fielddesc_getcoord(&x->x_viewbox[3].a_attr,
+            template, data, 0));
+        gui_end_array();
+    }
     // Not sure why display attr is here...
     gui_s("display");
     gui_s("inline");
@@ -4564,6 +4752,8 @@ static void draw_setup(void)
         gensym("stroke-width"), A_GIMME, 0);
     class_addmethod(svg_class, (t_method)svg_transform,
         gensym("transform"), A_GIMME, 0);
+    class_addmethod(svg_class, (t_method)svg_viewbox,
+        gensym("viewBox"), A_GIMME, 0);
     class_addmethod(svg_class, (t_method)svg_setattr,
         gensym("vis"), A_GIMME, 0);
     class_addmethod(svg_class, (t_method)svg_setattr,
@@ -4580,6 +4770,8 @@ static void draw_setup(void)
         gensym("y1"), A_GIMME, 0);
     class_addmethod(svg_class, (t_method)svg_setattr,
         gensym("y2"), A_GIMME, 0);
+    class_addmethod(svg_class, (t_method)svg_update_args,
+        gensym("update_svg"), A_GIMME, 0);
 }
 
 /* ------------------------------ event --------------------------------- */
@@ -5513,8 +5705,6 @@ static void plot_getgrouprect(t_glist *glist, t_template *elemtemplate,
     }
 } 
 
-
-
 static void plot_getrect(t_gobj *z, t_glist *glist,
     t_word *data, t_template *template, t_float basex, t_float basey,
     int *xp1, int *yp1, int *xp2, int *yp2)
@@ -5697,10 +5887,11 @@ static void plot_groupvis(t_scalar *x, t_glist *owner, t_word *data,
         (long unsigned int)data);
     sprintf(parent_tagbuf, "scelem%lx.%lx", (long unsigned int)parent,
         (long unsigned int)data);
-    gui_start_vmess("gui_create_scalar_group", "xss",
+    gui_start_vmess("gui_scalar_draw_group", "xsss",
         glist_getcanvas(owner),
         tagbuf,
-        parent_tagbuf);
+        parent_tagbuf,
+        "g");
     svg_grouptogui(groupcanvas, template, data);
     gui_end_vmess();
     for (y = groupcanvas->gl_list; y; y = y->g_next)
@@ -6325,148 +6516,660 @@ static void plot_setup(void)
     class_setparentwidget(plot_class, &plot_widgetbehavior);
 }
 
-/* ---------------- drawnumber: draw a number (or symbol) ---------------- */
-
-/*
-    drawnumbers draw numeric fields at controllable locations, with
-    controllable color and label.  invocation:
-    (drawnumber|drawsymbol) [-v <visible>] variable x y color label
-*/
-
-t_class *drawnumber_class;
-
-#define DRAW_SYMBOL 1
+/* --------- generic draw command for showing arrays --------------- */
 
-typedef struct _drawnumber
+static void *drawarray_new(t_symbol *s, int argc, t_atom *argv)
 {
-    t_object x_obj;
-    t_fielddesc x_value;
-    t_fielddesc x_xloc;
-    t_fielddesc x_yloc;
-    t_fielddesc x_color;
-    t_fielddesc x_vis;
-    t_fielddesc x_fontsize;
-    t_symbol *x_label;
-    int x_flags;
-    t_canvas *x_canvas;
-} t_drawnumber;
+    t_drawarray *x = (t_drawarray *)pd_new(drawarray_class);
 
-static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv)
-{
-    if (legacy_draw_in_group(canvas_getcurrent()))
-        return 0;
+    /* We need a t_svg to associate with it */
+    x->x_attr = (t_pd *)svg_new((t_pd *)x, s, 0, 0);
+    t_svg *sa = (t_svg *)x->x_attr;
 
-    t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class);
-    char *classname = classsym->s_name;
-    int flags = 0;
-    int got_font_size = 0;
-    
-    if (classname[4] == 's')
-        flags |= DRAW_SYMBOL;
-    x->x_flags = flags;
-    fielddesc_setfloat_const(&x->x_vis, 1);
     x->x_canvas = canvas_getcurrent();
-    while (1)
-    {
-        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);
-        if (!strcmp(firstarg->s_name, "-v") && argc > 1)
-        {
-            fielddesc_setfloatarg(&x->x_vis, 1, argv+1);
-            argc -= 2; argv += 2;
-        }
-        else break;
-    }
-    if (flags & DRAW_SYMBOL)
-    {
-        if (argc) fielddesc_setsymbolarg(&x->x_value, argc--, argv++);
-        else fielddesc_setsymbol_const(&x->x_value, &s_);
-    }
-    else
-    {
-        if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++);
-        else fielddesc_setfloat_const(&x->x_value, 0);
-    }
-    if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
-    else fielddesc_setfloat_const(&x->x_xloc, 0);
-    if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
-    else fielddesc_setfloat_const(&x->x_yloc, 0);
-    if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++);
-    else fielddesc_setfloat_const(&x->x_color, 1);
+    t_template *t = template_findbyname(
+        canvas_makebindsym(x->x_canvas->gl_name));
 
-    if (argc == 2)
-    {
-        fielddesc_setfloatarg(&x->x_fontsize, argc--, argv++);
-        got_font_size = 1;
-    }
-    if (argc)
-    {
-        if (argv->a_type == A_SYMBOL || got_font_size)
-        {
-            x->x_label = atom_getsymbolarg(0, argc, argv);
-            if (!got_font_size) 
-                fielddesc_setfloatarg(&x->x_fontsize, 0, NULL);         
-        }
-        else if (argv->a_type == A_FLOAT)
-        {
-            fielddesc_setfloatarg(&x->x_fontsize, argc, argv);
-            x->x_label = &s_;
-        }
-    } else {
-        fielddesc_setfloatarg(&x->x_fontsize, 0, NULL);
-        x->x_label = &s_;
-    }
+    if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++);
+    else fielddesc_setfloat_const(&x->x_data, 1);
+
+    /* Default dimensions for SVG: 150x100 */
+    if (argc) svg_attr_setfloatarg(&sa->x_width, argc--, argv++);
+    else svg_attr_setfloat_const(&sa->x_width, 150);
+    if (argc) svg_attr_setfloatarg(&sa->x_height, argc--, argv++);
+    else svg_attr_setfloat_const(&sa->x_height, 100);
 
     return (x);
 }
 
-void drawnumber_float(t_drawnumber *x, t_floatarg f)
+void drawarray_float(t_drawarray *x, t_floatarg f)
 {
-    int viswas;
-    if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var)
-    {
-        pd_error(x, "global vis/invis for a template with variable visibility");
-        return;
-    }
-    viswas = (x->x_vis.fd_un.fd_float != 0);
-    
-    if ((f != 0 && viswas) || (f == 0 && !viswas))
-        return;
-    canvas_redrawallfortemplatecanvas(x->x_canvas, 2);
-    fielddesc_setfloat_const(&x->x_vis, (f != 0));
-    canvas_redrawallfortemplatecanvas(x->x_canvas, 1);
+    /* toggle visibility on and off */
 }
 
-/* -------------------- widget behavior for drawnumber ------------ */
-
-/*#define DRAWNUMBER_BUFSIZE 80
-static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap)
+void drawarray_transform(t_drawarray *x, t_floatarg f)
 {
-    int nchars;
-    strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE);
-    buf[DRAWNUMBER_BUFSIZE - 1] = 0;
-    nchars = strlen(buf);
-    atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars);
-}*/
+    /* draw array uses an inner svg as the container, which
+       has no transform attribute */
+    pd_error(x, "draw array: no method for 'transform'");
+}
 
-static int drawnumber_gettype(t_drawnumber *x, t_word *data,
-    t_template *template, int *onsetp)
+static void drawarray_anything(t_drawarray *x, t_symbol *s, int argc,
+    t_atom *argv)
 {
-    int type;
-    t_symbol *arraytype;
-    if (template_find_field(template, /*x->x_fieldname*/ x->x_value.fd_un.fd_varsym, onsetp, &type,
-        &arraytype) && type != DT_ARRAY)
-            return (type);
-    else return (-1);
+    /* forward to t_svg thingy */
+    pd_typedmess(x->x_attr, s, argc, argv);
 }
 
-#define DRAWNUMBER_BUFSIZE 1024
-static void drawnumber_getbuf(t_drawnumber *x, t_word *data,
-    t_template *template, char *buf)
+/* -------------------- widget behavior for drawarray ------------ */
+
+
+    /* get everything we'll need from the owner template of the array being
+    drawn. Not used for garrays, but see below */
+static int drawarray_readownertemplate(t_drawarray *x,
+    t_word *data, t_template *ownertemplate, 
+    t_symbol **elemtemplatesymp, t_array **arrayp)
 {
-    int nchars, onset, type = drawnumber_gettype(x, data, template, &onset);
-    if (type < 0)
-        buf[0] = 0;
-    else
+    int arrayonset, type;
+    t_symbol *elemtemplatesym;
+    t_array *array;
+
+        /* find the data and verify it's an array */
+    if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var)
+    {
+        error("draw array: needs an array field");
+        return (-1);
+    }
+    if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym,
+        &arrayonset, &type, &elemtemplatesym))
+    {
+        error("draw array: %s: no such field", x->x_data.fd_un.fd_varsym->s_name);
+        return (-1);
+    }
+    if (type != DT_ARRAY)
+    {
+        error("draw array: %s: not an array", x->x_data.fd_un.fd_varsym->s_name);
+        return (-1);
+    }
+    array = *(t_array **)(((char *)data) + arrayonset);
+    *elemtemplatesymp = elemtemplatesym;
+    *arrayp = array;
+
+    return (0);
+}
+
+static void drawarray_getgrouprect(t_glist *glist, t_template *elemtemplate,
+    t_canvas *groupcanvas, int elemsize,
+    t_array *array, int i, t_float usexloc, t_float useyloc,
+    int *x1, int *y1, int *x2, int *y2)
+{
+    t_gobj *y;
+    for (y = groupcanvas->gl_list; y; y = y->g_next)
+    {
+        if (pd_class(&y->g_pd) == canvas_class &&
+            ((t_canvas *)y)->gl_svg)
+        {
+            drawarray_getgrouprect(glist, elemtemplate, (t_canvas *)y,
+                elemsize, array, i, usexloc, useyloc, x1, y1, x2, y2);
+        }
+        //fprintf(stderr,".-.-. usexloc %f useyloc %f "
+        //               "(alt %f %f)\n",
+        //  usexloc, useyloc,
+        //  basex + xloc +
+        //  fielddesc_cvttocoord(xfielddesc,
+        //      *(t_float *)(((char *)(array->a_vec) + elemsize * i)
+        //      + xonset)),
+        //  *(t_float *)(((char *)(array->a_vec) + elemsize * i) +
+        //  yonset));
+        int xx1, xx2, yy1, yy2;
+        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+        if (!wb) continue;
+        (*wb->w_parentgetrectfn)(y, glist,
+            (t_word *)((char *)(array->a_vec) + elemsize * i),
+                elemtemplate, usexloc, useyloc, 
+                    &xx1, &yy1, &xx2, &yy2);
+        //fprintf(stderr,"  .....drawarray_getrect %d %d %d %d\n",
+        //    xx1, yy1, xx2, yy2); 
+        if (xx1 < *x1)
+            *x1 = xx1;
+        if (yy1 < *y1)
+            *y1 = yy1;
+        if (xx2 > *x2)
+            *x2 = xx2;
+        if (yy2 > *y2)
+            *y2 = yy2;
+        //fprintf(stderr,"  ....drawarray_getrect %d %d %d %d\n",
+        //    x1, y1, x2, y2); 
+    }
+} 
+
+static void drawarray_getrect(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int *xp1, int *yp1, int *xp2, int *yp2)
+{
+    t_drawarray *x = (t_drawarray *)z;
+    int width, height, xoff, yoff;
+    int x1, y1, x2, y2;
+    x1 = y1 = 0x7fffffff;
+    x2 = y2 = -0x7fffffff;
+
+    t_float mtx1[3][3] = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} };
+    t_float mtx2[3][3] = { {1, 0, 0}, {0, 1, 0}, {1, 0, 1} };
+    t_float m1, m2, m3, m4, m5, m6,
+            tx1, ty1, tx2, ty2, t5, t6;
+    t_svg *sa = (t_svg *)x->x_attr;
+    if (!fielddesc_getfloat(&sa->x_bbox, template, data, 0) ||
+        (sa->x_vis.a_flag && !fielddesc_getfloat(&sa->x_vis.a_attr,
+            template, data, 0)))
+    {
+        *xp1 = *yp1 = 0x7fffffff;
+        *xp2 = *yp2 = -0x7fffffff;
+        return;
+    }
+
+    svg_groupmtx(sa, template, data, mtx1);
+    width = fielddesc_getcoord(&sa->x_width.a_attr, template, data, 0);
+    height = fielddesc_getcoord(&sa->x_height.a_attr, template, data, 0);
+    xoff = fielddesc_getcoord(&sa->x_x.a_attr, template, data, 0);
+    yoff = fielddesc_getcoord(&sa->x_y.a_attr, template, data, 0);
+ 
+    mset(mtx2, xoff, yoff, xoff + width, yoff + height, 0, 0);
+    mtx2[2][0] = 1; mtx2[2][1] = 1;
+    mmult(mtx1, mtx2, mtx2);
+    mget(mtx2, &tx1, &ty1, &tx2, &ty2, &t5, &t6);
+    if (tx1 < x1) x1 = tx1;
+    if (tx2 < x1) x1 = tx2;
+    if (ty1 < y1) y1 = ty1;
+    if (ty2 < y1) y1 = ty2;
+    if (tx1 > x2) x2 = tx1;
+    if (tx2 > x2) x2 = tx2;
+    if (ty1 > y2) y2 = ty1;
+    if (ty2 > y2) y2 = ty2;
+    mset(mtx2, xoff, yoff + height, xoff + width, yoff, 0, 0);
+    mtx2[2][0] = 1; mtx2[2][1] = 1;
+    mmult(mtx1, mtx2, mtx2);
+    mget(mtx2, &tx1, &ty1, &tx2, &ty2, &t5, &t6);
+    if (tx1 < x1) x1 = tx1;
+    if (tx2 < x1) x1 = tx2;
+    if (ty1 < y1) y1 = ty1;
+    if (ty2 < y1) y1 = ty2;
+    if (tx1 > x2) x2 = tx1;
+    if (tx2 > x2) x2 = tx2;
+    if (ty1 > y2) y2 = ty1;
+    if (ty2 > y2) y2 = ty2;
+    //x1 = glist_xtopixels(glist, basex + x1);
+    //x2 = glist_xtopixels(glist, basex + x2);
+    //y1 = glist_ytopixels(glist, basey + y1);
+    //y2 = glist_ytopixels(glist, basey + y2);
+
+    x1 = basex + x1;
+    x2 = basex + x2;
+    y1 = basey + y1;
+    y2 = basey + y2;
+
+    /* todo: put these up top */
+    if (!fielddesc_getfloat(&sa->x_vis.a_attr, template, data, 0))
+    {
+        *xp1 = *yp1 = 0x7fffffff;
+        *xp2 = *yp2 = -0x7fffffff;
+        return;
+    }
+    *xp1 = x1;
+    *yp1 = y1;
+    *xp2 = x2;
+    *yp2 = y2;
+}
+
+static void drawarray_displace(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int dx, int dy)
+{
+        /* not yet */
+}
+
+static void drawarray_select(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int state)
+{
+    //fprintf(stderr,"drawarray_select %d\n", state);
+    /* not yet */
+}
+
+static void drawarray_activate(t_gobj *z, t_glist *glist,
+    t_word *data, t_template *template, t_float basex, t_float basey,
+    int state)
+{
+        /* not yet */
+}
+
+static void drawarray_groupvis(t_scalar *x, t_glist *owner, t_word *data,
+    t_template *template,
+    t_glist *groupcanvas, t_glist *parent, t_float basex, t_float basey)
+{
+    t_gobj *y;
+    char tagbuf[MAXPDSTRING], parent_tagbuf[MAXPDSTRING];
+    sprintf(tagbuf, "scelem%lx.%lx", (long unsigned int)groupcanvas,
+        (long unsigned int)data);
+    sprintf(parent_tagbuf, "scelem%lx.%lx", (long unsigned int)parent,
+        (long unsigned int)data);
+    gui_start_vmess("gui_scalar_draw_group", "xsss",
+        glist_getcanvas(owner),
+        tagbuf,
+        parent_tagbuf,
+        "g");
+    svg_grouptogui(groupcanvas, template, data);
+    gui_end_vmess();
+    for (y = groupcanvas->gl_list; y; y = y->g_next)
+    {
+        if (pd_class(&y->g_pd) == canvas_class &&
+            ((t_glist *)y)->gl_svg)
+        {
+            drawarray_groupvis(x, owner, data, template, (t_glist *)y,
+                groupcanvas, basex, basey);
+        }
+        t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+        if (!wb) continue;
+        (*wb->w_parentvisfn)(y, owner, groupcanvas, x, data, template,
+            basex, basey, 1);
+    }
+}
+
+/* todo: merge this with plot_has_drawcommand */
+/* see if the elements we're plotting have any drawing commands */
+int drawarray_has_drawcommand(t_canvas *elemtemplatecanvas)
+{
+    t_gobj *y;
+    for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
+    {
+        if (pd_class(&y->g_pd) == canvas_class && ((t_glist *)y)->gl_svg)
+            return 1;
+        else if (class_isdrawcommand(y->g_pd))
+            return 1;
+    }
+    return 0;
+}
+
+static void drawarray_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
+    t_scalar *sc, t_word *data, t_template *template,
+    t_float basex, t_float basey, int tovis)
+{
+    t_drawarray *x = (t_drawarray *)z;
+    int elemsize, yonset, wonset, xonset, i;
+    t_canvas *elemtemplatecanvas;
+    t_template *elemtemplate;
+    t_symbol *elemtemplatesym;
+    t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc;
+    /* Let's just set constant increment values and see how they
+       play out before adding xinc and yinc to the public interface... */
+    t_float xinc = 40, yinc = 40, xsum, yval;
+    t_array *array;
+    int nelem;
+    char *elem;
+        
+    if (drawarray_readownertemplate(x, data, template, 
+        &elemtemplatesym, &array)
+            || array_getfields(elemtemplatesym, &elemtemplatecanvas,
+                &elemtemplate, &elemsize, 0, 0, 0,
+                &xonset, &yonset, &wonset))
+                    return;
+    nelem = array->a_n;
+    elem = (char *)array->a_vec;
+
+    /* id for the the viewport-- we prefix it with "draw" to be
+       compatible with the other svg-based drawcommands */
+    char viewport_tagbuf[MAXPDSTRING];
+    sprintf(viewport_tagbuf, "draw%lx.%lx",
+        (long unsigned int)x, (long unsigned int)data);
+
+    if (tovis)
+    {
+        int in_array = (sc->sc_vec == data) ? 0 : 1;
+        int draw_scalars = plot_has_drawcommand(elemtemplatecanvas);
+
+        /* make sure the array drawings are behind the graph */
+        /* not doing this yet with the GUI port... */
+        //sys_vgui(".x%lx.c lower plot%lx graph%lx\n", glist_getcanvas(glist),
+        //    data, glist);
+
+        /* 1. Set up the main <g> for this widget */
+        char parent_tagbuf[MAXPDSTRING];
+        if (in_array)
+        {
+            sprintf(parent_tagbuf, "scelem%lx.%lx",
+                (long unsigned int)parentglist,
+                (long unsigned int)data);
+        }
+        else
+        {
+            sprintf(parent_tagbuf, "dgroup%lx.%lx",
+                (long unsigned int)x->x_canvas,
+                (long unsigned int)data);
+        }
+
+        t_svg *sa = (t_svg *)x->x_attr;
+        gui_start_vmess("gui_draw_vis", "xs",
+            glist_getcanvas(glist), "g");
+        svg_togui(sa, template, data);
+
+        gui_start_array();
+        gui_s(parent_tagbuf);
+        gui_s(viewport_tagbuf);
+        gui_end_array();
+        gui_end_vmess();
+
+            /* 2. Draw the individual elements */
+            /* This code is inefficient since the template has to be
+            searched for drawing instructions for every last point. */
+        if (draw_scalars)
+        {
+            //t_float xoffset = in_array ? basex: 0;
+            //t_float yoffset = in_array ? basey: 0;
+            t_float xoffset = 0;
+            t_float yoffset = 0;
+
+            for (xsum = 0, i = 0; i < nelem; i++)
+            {
+                t_float usexloc, useyloc;
+                t_gobj *y;
+                if (xonset >= 0)
+                    usexloc = xoffset +
+                        *(t_float *)((elem + elemsize * i) + xonset);
+                else usexloc = xoffset + xsum, xsum += xinc;
+                if (yonset >= 0)
+                    yval = *(t_float *)((elem + elemsize * i) + yonset);
+                else yval = 0;
+                useyloc = yoffset + yval;
+                /*    fielddesc_cvttocoord(yfielddesc, yval); */
+                 /* We're setting up a special group that will get set as
+                   the parent by array elements */
+
+                   /* todo: need to check if drawarray itself is in an array */
+                char tagbuf[MAXPDSTRING];
+                sprintf(tagbuf, "scelem%lx.%lx",
+                    (long unsigned int)elemtemplatecanvas,
+                    (long unsigned int)((t_word *)(elem + elemsize * i)));
+                char transform_buf[MAXPDSTRING];
+                sprintf(transform_buf, "translate(%g,%g)", usexloc, useyloc);
+
+                gui_start_vmess("gui_draw_vis", "xs",
+                    glist_getcanvas(glist), "g");
+                gui_start_array();
+                gui_s("transform");
+                gui_s(transform_buf);
+                gui_end_array();
+                gui_start_array();
+                gui_s(viewport_tagbuf);
+                gui_s(tagbuf);
+                gui_end_array();
+                gui_end_vmess();
+
+                for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
+                {
+                    if (pd_class(&y->g_pd) == canvas_class &&
+                        ((t_glist *)y)->gl_svg)
+                    {
+                        drawarray_groupvis(sc, glist,
+                            (t_word *)(elem + elemsize * i),
+                        template, (t_glist *)y, 
+                            elemtemplatecanvas, usexloc, useyloc);
+                    }
+                    t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+                    if (!wb) continue;
+                    (*wb->w_parentvisfn)(y, glist, elemtemplatecanvas, sc,
+                        (t_word *)(elem + elemsize * i),
+                            elemtemplate, usexloc, useyloc, tovis);
+                }
+            }
+        }
+        if (!glist_istoplevel(glist))
+        {
+            t_canvas *gl = glist_getcanvas(glist);
+            char objtag[64];
+            sprintf(objtag, ".x%lx.x%lx.template%lx",
+                (t_int)gl, (t_int)glist, (t_int)data);
+            canvas_restore_original_position(gl, (t_gobj *)glist, objtag, -1);
+        }
+    }
+    else
+    {
+        /* un-draw the individual points */
+        //fprintf(stderr,"drawarray_vis UNVIS\n");
+
+        int i;
+        for (i = 0; i < nelem; i++)
+        {
+            t_gobj *y;
+            for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
+            {
+                t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
+                if (!wb) continue;
+                (*wb->w_parentvisfn)(y, glist, elemtemplatecanvas, sc,
+                    (t_word *)(elem + elemsize * i), elemtemplate,
+                        0, 0, 0);
+            }
+        }
+        /* Now remove our drawarray svg container */
+        gui_vmess("gui_draw_erase_item", "xs", glist_getcanvas(glist),
+            viewport_tagbuf);
+    }
+}
+
+static int drawarray_click(t_gobj *z, t_glist *glist, 
+    t_word *data, t_template *template, t_scalar *sc, t_array *ap,
+    t_float basex, t_float basey,
+    int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+/* Let's hold off on this for a bit...
+    //fprintf(stderr,"drawarray_click %lx %lx %f %f %d %d\n",
+    //    (t_int)z, (t_int)glist, basex, basey, xpix, ypix);
+    t_drawarray *x = (t_drawarray *)z;
+    t_symbol *elemtemplatesym;
+    t_float linewidth, xloc, xinc, yloc, style, vis, scalarvis;
+    t_array *array;
+    t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc;
+    t_symbol *symfillcolor;
+    t_symbol *symoutlinecolor;
+
+    if (!drawarray_readownertemplate(x, data, template, 
+        &elemtemplatesym, &array)
+        && (vis != 0))
+    {
+    //fprintf(stderr,"  ->array_doclick\n");
+        return (array_doclick(array, glist, sc, ap,
+            elemtemplatesym,
+            linewidth, basex + xloc, xinc, basey + yloc, scalarvis,
+            xfielddesc, yfielddesc, wfielddesc,
+            xpix, ypix, shift, alt, dbl, doit));
+    }
+    else return (0);
+*/
+    return 0;
+}
+
+static void drawarray_free(t_drawarray *x)
+{
+    //sys_queuegui(x->x_canvas, 0, canvas_redrawallfortemplatecanvas);
+    /* decrement variable of the template
+       to prevent transform as that would
+       make arrays break their hitboxes
+       and all kinds of other bad stuff */
+    t_template *t = template_findbyname(
+        canvas_makebindsym(x->x_canvas->gl_name)
+    );
+    if (t)
+    {
+        t->t_transformable--;
+        //fprintf(stderr,"drawarray_free > template:%lx(%s) transform:%d\n",
+        //    (t_int)t, canvas_makebindsym(x->x_canvas->gl_name)->s_name,
+        //    t->t_transformable);
+    }
+}
+
+t_parentwidgetbehavior drawarray_widgetbehavior =
+{
+    drawarray_getrect,
+    drawarray_displace,
+    drawarray_select,
+    drawarray_activate,
+    drawarray_vis,
+    drawarray_click,
+};
+
+static void drawarray_setup(void)
+{
+    drawarray_class = class_new(gensym("drawarray"),
+        0,
+        (t_method)drawarray_free, sizeof(t_drawarray), 0, A_GIMME, 0);
+    class_setdrawcommand(drawarray_class);
+    class_addfloat(drawarray_class, drawarray_float);
+    class_addmethod(drawarray_class, (t_method)drawarray_transform,
+        gensym("transform"), A_GIMME, 0);
+    class_addanything(drawarray_class, drawarray_anything);
+    class_setparentwidget(drawarray_class, &drawarray_widgetbehavior);
+}
+
+/* ---------------- drawnumber: draw a number (or symbol) ---------------- */
+
+/*
+    drawnumbers draw numeric fields at controllable locations, with
+    controllable color and label.  invocation:
+    (drawnumber|drawsymbol) [-v <visible>] variable x y color label
+*/
+
+t_class *drawnumber_class;
+
+#define DRAW_SYMBOL 1
+
+typedef struct _drawnumber
+{
+    t_object x_obj;
+    t_fielddesc x_value;
+    t_fielddesc x_xloc;
+    t_fielddesc x_yloc;
+    t_fielddesc x_color;
+    t_fielddesc x_vis;
+    t_fielddesc x_fontsize;
+    t_symbol *x_label;
+    int x_flags;
+    t_canvas *x_canvas;
+} t_drawnumber;
+
+static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv)
+{
+    if (legacy_draw_in_group(canvas_getcurrent()))
+        return 0;
+
+    t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class);
+    char *classname = classsym->s_name;
+    int flags = 0;
+    int got_font_size = 0;
+    
+    if (classname[4] == 's')
+        flags |= DRAW_SYMBOL;
+    x->x_flags = flags;
+    fielddesc_setfloat_const(&x->x_vis, 1);
+    x->x_canvas = canvas_getcurrent();
+    while (1)
+    {
+        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);
+        if (!strcmp(firstarg->s_name, "-v") && argc > 1)
+        {
+            fielddesc_setfloatarg(&x->x_vis, 1, argv+1);
+            argc -= 2; argv += 2;
+        }
+        else break;
+    }
+    if (flags & DRAW_SYMBOL)
+    {
+        if (argc) fielddesc_setsymbolarg(&x->x_value, argc--, argv++);
+        else fielddesc_setsymbol_const(&x->x_value, &s_);
+    }
+    else
+    {
+        if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++);
+        else fielddesc_setfloat_const(&x->x_value, 0);
+    }
+    if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
+    else fielddesc_setfloat_const(&x->x_xloc, 0);
+    if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
+    else fielddesc_setfloat_const(&x->x_yloc, 0);
+    if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++);
+    else fielddesc_setfloat_const(&x->x_color, 1);
+
+    if (argc == 2)
+    {
+        fielddesc_setfloatarg(&x->x_fontsize, argc--, argv++);
+        got_font_size = 1;
+    }
+    if (argc)
+    {
+        if (argv->a_type == A_SYMBOL || got_font_size)
+        {
+            x->x_label = atom_getsymbolarg(0, argc, argv);
+            if (!got_font_size) 
+                fielddesc_setfloatarg(&x->x_fontsize, 0, NULL);         
+        }
+        else if (argv->a_type == A_FLOAT)
+        {
+            fielddesc_setfloatarg(&x->x_fontsize, argc, argv);
+            x->x_label = &s_;
+        }
+    } else {
+        fielddesc_setfloatarg(&x->x_fontsize, 0, NULL);
+        x->x_label = &s_;
+    }
+
+    return (x);
+}
+
+void drawnumber_float(t_drawnumber *x, t_floatarg f)
+{
+    int viswas;
+    if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var)
+    {
+        pd_error(x, "global vis/invis for a template with variable visibility");
+        return;
+    }
+    viswas = (x->x_vis.fd_un.fd_float != 0);
+    
+    if ((f != 0 && viswas) || (f == 0 && !viswas))
+        return;
+    canvas_redrawallfortemplatecanvas(x->x_canvas, 2);
+    fielddesc_setfloat_const(&x->x_vis, (f != 0));
+    canvas_redrawallfortemplatecanvas(x->x_canvas, 1);
+}
+
+/* -------------------- widget behavior for drawnumber ------------ */
+
+/*#define DRAWNUMBER_BUFSIZE 80
+static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap)
+{
+    int nchars;
+    strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE);
+    buf[DRAWNUMBER_BUFSIZE - 1] = 0;
+    nchars = strlen(buf);
+    atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars);
+}*/
+
+static int drawnumber_gettype(t_drawnumber *x, t_word *data,
+    t_template *template, int *onsetp)
+{
+    int type;
+    t_symbol *arraytype;
+    if (template_find_field(template, /*x->x_fieldname*/ x->x_value.fd_un.fd_varsym, onsetp, &type,
+        &arraytype) && type != DT_ARRAY)
+            return (type);
+    else return (-1);
+}
+
+#define DRAWNUMBER_BUFSIZE 1024
+static void drawnumber_getbuf(t_drawnumber *x, t_word *data,
+    t_template *template, char *buf)
+{
+    int nchars, onset, type = drawnumber_gettype(x, data, template, &onset);
+    if (type < 0)
+        buf[0] = 0;
+    else
     {
         strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE);
         buf[DRAWNUMBER_BUFSIZE - 1] = 0;
@@ -7331,9 +8034,8 @@ static void *drawimage_new(t_symbol *classsym, int argc, t_atom *argv)
     t_drawimage *x = (t_drawimage *)pd_new(drawimage_class);
 
     /* we need a t_svg to associate with it */
-    x->x_attr = (t_pd *)svg_new((t_pd *)x, classsym, 0, 0);
-
-    x->x_deleteme = 0;
+    t_svg *sa = (t_svg *)svg_new((t_pd *)x, classsym, 0, 0);
+    
     char *classname = classsym->s_name;
     char buf[50];
     sprintf(buf, "x%lx", (t_int)x);
@@ -7343,16 +8045,17 @@ static void *drawimage_new(t_symbol *classsym, int argc, t_atom *argv)
     if (classname[0] == 's')
         flags |= DRAW_SPRITE;
     x->x_flags = flags;
+    x->x_attr = (t_pd *)sa;
     fielddesc_setfloat_const(&x->x_vis, 1);
     x->x_canvas = canvas_getcurrent();
     t_symbol *dir = canvas_getdir(x->x_canvas);
     if (argc && argv->a_type == A_SYMBOL)
         x->x_img = atom_getsymbolarg(0, argc--, argv++);
     else x->x_img = &s_;
-    if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
-    else fielddesc_setfloat_const(&x->x_xloc, 0);
-    if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
-    else fielddesc_setfloat_const(&x->x_yloc, 0);
+    if (argc) svg_attr_setfloatarg(&sa->x_x, argc--, argv++);
+    else svg_attr_setfloat_const(&sa->x_x, 0);
+    if (argc) svg_attr_setfloatarg(&sa->x_y, argc--, argv++);
+    else svg_attr_setfloat_const(&sa->x_y, 0);
     /* [drawimage] allocates memory for an image or image sequence
        while the object is creating. The corresponding scalar gets
        drawn as a canvas image item using the "parent" tk image as
@@ -7364,7 +8067,6 @@ static void *drawimage_new(t_symbol *classsym, int argc, t_atom *argv)
         x->x_img->s_name,
         dir->s_name,
         x->x_flags);
-    //post("deleteme is %d", x->x_deleteme);
     return (x);
 }
 
@@ -7412,31 +8114,31 @@ void drawimage_symbol(t_drawimage *x, t_symbol *s)
     drawimage_index(x, 0, 1, at); 
 }
 
-static void drawimage_x(t_drawimage *x, t_symbol *s, int argc,
+static void drawimage_forward(t_drawimage *x, t_symbol *s, int argc,
     t_atom *argv)
 {
-    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
-    {
-        fielddesc_setfloatarg(&x->x_xloc, argc, argv);
-        canvas_redrawallfortemplatecanvas(x->x_canvas, 0);
-    }
+    /* forward to t_svg thingy */
+    pd_typedmess(x->x_attr, s, argc, argv);
 }
 
-static void drawimage_y(t_drawimage *x, t_symbol *s, int argc,
+/* With the current drawimage/sprite implementation we can't easily support
+   the x and y attributes. The reason is that we're currently just applying
+   attributes to the parent <g> for convenience, but <g> has no x/y atty.
+
+   We could just forward everything to the child <image> element, but that
+   could get clunky when dealing with large image sequences. So for now we
+   just disallow setting the x/y with the knowledge that the user can get
+   the same functionality using a transform. */
+static void drawimage_x(t_drawimage *x, t_symbol *s, int argc,
     t_atom *argv)
 {
-    if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
-    {
-        fielddesc_setfloatarg(&x->x_yloc, argc, argv);
-        canvas_redrawallfortemplatecanvas(x->x_canvas, 0);
-    }
+    pd_error(x, "draw: x attribute for image type not supported");
 }
 
-static void drawimage_forward(t_drawimage *x, t_symbol *s, int argc,
+static void drawimage_y(t_drawimage *x, t_symbol *s, int argc,
     t_atom *argv)
 {
-    /* forward to t_svg thingy */
-    pd_typedmess(x->x_attr, s, argc, argv);
+    pd_error(x, "draw: y attribute for image type not supported");
 }
 
 static void drawimage_anything(t_drawimage *x, t_symbol *s, int argc,
@@ -7492,8 +8194,8 @@ static void drawimage_getrect(t_gobj *z, t_glist *glist,
         &m4, &m5, &m6);
     mset(mtx2, m1, m2, m3, m4, m5, m6);
     mmult(mtx1, mtx2, mtx1);
-    xloc = fielddesc_getcoord(&x->x_xloc, template, data, 0);
-    yloc = fielddesc_getcoord(&x->x_yloc, template, data, 0);
+    xloc = fielddesc_getcoord(&sa->x_x.a_attr, template, data, 0);
+    yloc = fielddesc_getcoord(&sa->x_y.a_attr, template, data, 0);
  
     mset(mtx2, xloc, yloc, xloc + x->x_w, yloc + x->x_h, 0, 0);
     mtx2[2][0] = 1; mtx2[2][1] = 1;
@@ -7578,13 +8280,15 @@ static void drawimage_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
     {
         int in_array = (sc->sc_vec == data) ? 0: 1;
         /*int xloc = glist_xtopixels(glist,
-            basex + fielddesc_getcoord(&x->x_xloc, template, data, 0));
+            basex + fielddesc_getcoord(&svg->x_x.a_attr, template, data, 0));
         int yloc = glist_ytopixels(glist,
-            basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));
+            basey + fielddesc_getcoord(&svg->x_y.a_attr, template, data, 0));
         sys_vgui("pdtk_drawimage_vis .x%lx.c %d %d .x%lx .x%lx.i %d ",*/
-        int xloc = fielddesc_getcoord(&x->x_xloc, template, data, 0);
-        int yloc = fielddesc_getcoord(&x->x_yloc, template, data, 0);
+        t_float xloc = fielddesc_getcoord(&svg->x_x.a_attr, template, data, 0);
+        t_float yloc = fielddesc_getcoord(&svg->x_y.a_attr, template, data, 0);
 
+fprintf(stderr, "xloc is %g\n", xloc);
+fprintf(stderr, "yloc is %g\n", yloc);
         char tagbuf[MAXPDSTRING];
         char parent_tagbuf[MAXPDSTRING];
         sprintf(tagbuf, "draw%lx.%lx",
@@ -7594,7 +8298,7 @@ static void drawimage_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
             in_array ? (long unsigned int)parentglist : (long unsigned int)parent,
             (long unsigned int)data);
 
-        gui_vmess("gui_drawimage_vis", "xiixxis",
+        gui_vmess("gui_drawimage_vis", "xffxxis",
             glist_getcanvas(glist),
             xloc,
             yloc,
@@ -7846,24 +8550,19 @@ t_canvas *canvas_templatecanvas_forgroup(t_canvas *c)
 {
     t_canvas *templatecanvas = c;
     if (!c->gl_owner)
-    {
-    return templatecanvas;
-    }
+        return templatecanvas;
 
    /* warning: this needs to be carefully considered-- seems like
       canvas's struct may not be initialized before the objects within
       it. */
     t_binbuf *b = c->gl_obj.te_binbuf;
     if (!b)
-    {
         return c;
-    }
     t_atom *argv = binbuf_getvec(b);
     if (binbuf_getnatom(b) > 1 &&
-        argv[0].a_type == A_SYMBOL &&
-        argv[0].a_w.w_symbol == gensym("draw") &&
-        argv[1].a_type == A_SYMBOL &&
-        argv[1].a_w.w_symbol == gensym("g"))
+        atom_getsymbol(argv) == gensym("draw") &&
+        (atom_getsymbol(argv+1) == gensym("g") ||
+         atom_getsymbol(argv+1) == gensym("svg")))
     {
         templatecanvas = canvas_templatecanvas_forgroup(c->gl_owner);
     }
@@ -7886,6 +8585,8 @@ t_template *template_findbydrawcommand(t_gobj *g)
         c = ((t_drawimage *)g)->x_canvas;
     else if (g->g_pd == plot_class)
         c = ((t_plot *)g)->x_canvas;
+    else if (g->g_pd == drawarray_class)
+        c = ((t_drawarray *)g)->x_canvas;
     else if (g->g_pd == canvas_class)
         c = (t_canvas *)g;
     else return (0);
@@ -7963,5 +8664,6 @@ void g_template_setup(void)
     drawnumber_setup();
     drawsymbol_setup();
     drawimage_setup();
+    drawarray_setup();
 }
 
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index 074d5aed7b4bb9ee06f5713abdf3858458c88d4e..6b735f3ac19c3080b04addce650589dbc7204b46 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -3018,6 +3018,10 @@ void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize, int pos)
                     (void *)canvas_undo_set_recreate(glist_getcanvas(glist),
                     &x->te_g, pos));
                 typedmess(&x->te_pd, gensym("rename"), natom2-1, vec2+1);
+                // Special case for [draw svg] -- update the args
+                if (((t_canvas *)x)->gl_svg)
+                    typedmess(((t_canvas *)x)->gl_svg, gensym("update_svg"),
+                        natom2-1, vec2+1);
                 binbuf_free(x->te_binbuf);
                 x->te_binbuf = b;
                 glob_preset_node_list_seek_hub();