A proposal for adding declarative drawing to SVG

<replicate> is to space as <animate> is to time 
(sort of)

Dailey and Eric Elder

  1. richer classes of gradients,
  2. extensions to new classes of patterns,
  3. new types of 2.5 dimensional objects.
  4. Authors want it!
  1. Implementers: many browsers and other viewers will already have some of the relevant code base developed,
  2. Authors: those conversant with SMIL will find its acquisition easy,
  3. Coding: that code written using such tags will be concise and succinct,
  4. Development time:   development time for rich, complex and useful graphics will be shortened.

Case 1: Simple replication of an SVGObject over a single attribute using "from" and "to"
Case 2: Replication of an SVGObject over multiple attributes using "from" and "to"
Case 3: Interpolation over multiple lists of values
Case 4: Replicating while rotating, scaling or translating
Case 5: Interpolating across multiple color values
Case 6: Path interpolation
Case 7. Using paths to define attribute values (extending the SMIL concepts of animateMotion and mpath)
Case 8: Using <replicateModifier> to clone and adjust gradients, animations, patterns, filters, clipPaths, and masks as we replicate
Case 9: Nested replications; Defining more complex textures and patterns

Background - frustrations with gradients, tilings, lack of 3D, adjacency.

Figure 1 aFigure 1 bFigure 1 cFigure 1 dFigure 1 eFigure 1 fFigure 1 gFigure 1 h

Figure 1: Gradients using (from left to right and top to bottom): transparency, masks, displacement filter, tessellation, tessellation, script, script, and gradient-font.

2005 Frustration with <pattern> http://www.mail-archive.com/svg-developers@yahoogroups.com/msg07277.html

2006 Frustration with gradient

  1. INRIA who were encountering a similar problem in their attempts to visualize systems of partial differential equations using smooth gradient textures [2]
  2. Andrew Matseevsky wrote several small articles to the svg-developers group talking about his own approach to advanced gradients.[11]
  3. In an attempt to simulate a richer range of gradients types than what SVG afforded, I experimented with the overlay of gradients using transparency [3], masks [4], filter displacements [5], tessellations [6,7], script [8,9] and even with defining a font made of an alphabet of piecewise linear gradients, stretched to fit a Bezier-curve using <textPath> [10]

2007,  Frustration with <path> -- (svg developers) not relative,  Turtle graphics, microsyntax

2008,  Attempts to solve

  1. SVGOpen  [12] on various problems on the "edges of SVG." Further description of some of these approaches may be seen therein.
  2. Adobe and INRIA in 2008 published their paper on diffusion curves [13]
  3. and Israel Eisenberg circulated several papers including his "tubefy" effect [14] using a script-based approach to generating gradients that are neither linear nor radial. 
  4.  web-essay [15] about several possible extensions to the SVG spec,  <contour> <doodle> <superPath> <replicate>

New solution

  1. <contour> would tween paths over space = motion
  2. <animate> =SVG/SMIL already describes motion
  3. script-free ways of choreographing changes (over time) in a features of  SVG objects [20]
  4. script-free ways of choreographing changes (over space) in a features of  SVG objects [20]

 instead of changing a single object over time, we "keep" each of the stages of its evolution and lay them down in the document atop one another. This would be done through  a new <replicate> that would be rather like a group <g>. Accordingly, the solution as envisioned would be a lot like SVG's <use> but on steroids.

Differences between time and space

  1.  We use time for planning, playing and reminiscing, and we use space for storing junk automobiles. 
  2. Time and space don't feel quite the same. 
  3. Data analysts are often quite comfortable with conversions between time and space.

Can the same content be replicated and animated?

attributes that might be animated [19] -- would we replicate???

  1. The cx, cy, rx and ry of an ellipse
  2. The height of an image ???
  3. The opacity of an image ??? 
  4. The scale of an feDisplacementMap ??? 
  5. The focal points and radius of a reflected radial gradient
  6. The offset and stop-opacity of a stop in a radial gradient
  7. The baseFrequency or seed of an feTurbulence ??? [Making the grain of floor tiles denser as they recede into the distance; changing the grain of adjacent tiles]
  8. The height of a rectangle contained in a pattern
  9. The values of an feColorMatrix ??? [simulating lighting effects as we move a texture from foreground to background]

As with attributes that are animateable, why not make things replicable?

Animation: two attributes can have different time signatures
Replication: two attributes must be viewed concurrently within a spatial instance.


< SVGObject >
<replicate attributeName="P" spatialfrequency="7" values="x,y,z"/>
<replicate attributeName="Q" spatialfrequency="12" values="a,b,c"/>
< /SVGObject >


< SVGObject >
<replicate repeatCount="12">
<replicateAttribute attributeName="P" values="x,y,z"/>
<replicateAttribute attributeName="Q" values="a,b,c"/>
< /SVGObject >

Proof of Concept

We have built a JavaScript interpreter that works in the following general way, with some notable exceptions *[24].

<replicate> tags are parsed in JavaScript using something resembling *[25] this

var ReplicateElements=document.getElementsByTagNameNS(svgr,"replicate");
for (var i=0;i<ReplicateElements.length;i++){ //loop through each replicate tag
//modify each node being replicated


Illustration 1.1
<ellipse rx="80" ry="50" cy="-100" cx="200" fill="url(#f)" stroke-width=".5" stroke="none" > 
<replicate repeatCount="50" >
<replicateAttribute attributeName="cy" from="300" to="100" />

Illustration 2.1
<text font-size="12" id="WWW" x="-50" y="300" fill="red"> 
<replicate id="F" repeatCount="80">
<replicateAttribute attributeName="y" from="150" to="290"/>
<replicateAttribute attributeName="x" from="90" to="200"/>
<replicateAttribute attributeName="font-size" from="9" to="170"/>
<replicateAttribute attributeName="fill"
from="rgb(0,0,0)" to="rgb(0,123,255)" />
Illustration 2.2
<ellipse rx="100" ry="150" fill="rgb(255,200,0)" 
cx="-160" cy="200">
<replicate repeatCount="75" >
<replicateAttribute attributeName="cx" from="400" to="350" />
<replicateAttribute attributeName="cy" from="200" to="300" />
<replicateAttribute attributeName="ry" from="150" to="65" />
<replicateAttribute attributeName="rx" from="100" to="150" />
<replicateAttribute attributeName="opacity" from="1" to=".75" />
<replicateAttribute attributeName="stroke-opacity" from=".5" to="0" />
<replicateAttribute attributeName="stroke-width" from="4" to="0" />
<replicateAttribute attributeName="stroke" from="rgb(100,255,120)" to="rgb(0,124,124)" />
<replicateAttribute attributeName="fill" from="rgb(0,128,255)" to="rgb(225,255,220)" />

Case 3: Interpolation over multiple lists of values

Illustration 3.1
<ellipse cy="-50" fill="url(#f)" stroke="none" > 
<replicate repeatCount="190" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="cx" values="450; 250; 350" />
<replicateAttribute attributeName="ry" values="100;20;20;150;0" />
<replicateAttribute attributeName="rx" values="150;200;0" />
<replicateAttribute attributeName="opacity" values="1;1;0" />
<ellipse cx="300" cy="50" fill="url(#g)" stroke-width=".5" stroke="none" > 
<replicate repeatCount="70" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="ry" values="120;10" />
<replicateAttribute attributeName="rx" values="50;200;60;70;50" />
Illustration 3.3
<rect x="-200" width="40" height="40" 
<replicate repeatCount="200">
<replicateAttribute attributeName="y"
values="200;50;200;50;200;50;200;50" />
<replicateAttribute attributeName="x" values="40;600;40;600" />
Illustration 3.4
curved shape with handle
<ellipse cy="-1000" cx="400" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >
<replicate repeatCount="180" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="transform"
from="rotate(0 400 200)" to="rotate(180 400 200)" />
<replicateAttribute attributeName="ry" from="100" to="15" />
<replicateAttribute attributeName="rx" from="100" to="10" />
unfolding leaves

<ellipse cx="200" cy="-50" rx="30" ry="50" fill="url(#f)" stroke-width=".5" stroke="none" >
<replicate repeatCount="100" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="ry" from="100" to="0" />
<replicateAttribute attributeName="rx" from="100" to="0" />
<replicateAttribute attributeName="stroke-width" from="0" to="3" />
<replicateAttribute attributeName="fill"
values=" rgb(255,200,200);rgb(255,255,0);rgb(0,255,0);
rgb(0,255,255); rgb(0,0,255); rgb(255,0,255); rgb(255,255,255)" />
Illustration 5.2
<ellipse cy="200" cx="400" rx="30" ry="50">
<replicate repeatCount="120" >
<replicateAttribute attributeName="cy" from="250" to="170" />
<replicateAttribute attributeName="cx" from="400" to="350" />
<replicateAttribute attributeName="transform"
from="rotate(0 300 200)" to="rotate(900 300 200)" />
<replicateAttribute attributeName="ry" from="100" to="25" />
<replicateAttribute attributeName="rx" from="100" to="30" />
<replicateAttribute attributeName="fill"
values=" rgb(255,0,200);rgb(255,255,0);rgb(0,255,0);
rgb(0,255,255); rgb(0,0,255); rgb(255,0,255);
rgb(255,255,255)" />

<contour> proposal is subsumed by <replicate>

Interpolation between two quadrilaterals
<path d="M -10 -10 -20 -20 z" fill-opacity=".4" stroke="black" 
stroke-width=".3" stroke-opacity=".3">
<replicate repeatCount="80" >
<replicateAttribute attributeName="d"
from="M 411 73, 487 201, 420 376, 217 240 z"
to="M 511 73, 387 201, 520 376, 317 240 z"
<replicateAttribute attributeName="fill"
from="rgb(45,45,128)" to="rgb(0,233,128)" />
Interpolation across shapes and colors
<path d="M -10 -10 -20 -20 z"
stroke="red" stroke-width=".3" stroke-opacity=".2">
<replicate repeatCount="40" >
<replicateAttribute attributeName="d"
from="M 511 73, C 387 201, 320 376, 117 240 C 287 201, 20 76, 511 73z"
to= "M 411 93, C 220 151, 190 276, 207 250 C 289 101, 230 126, 411 93z"
<replicateAttribute attributeName="fill"


Given a path (defined typically inside the <defs> part of a document) :

<path id="curv" d=pathdata />

We may use that path to define pairs of numeric attibutes of another SVGObject as follows:

<replicate repeatCount="n" >
<replicatePath xlink:href="#curv" xattribute="att1" yattribute="att2"/>
<replicatePath xlink:href="#curv" xattribute="p * att3 + q" yattribute="att4"/>
<replicateAttribute attributeName="att5" from="x1" to="x2" />
<replicateAttribute attributeName="att6" from="y1" to="y2" />
<replicateAttribute attributeName="att7" values="z1;z2;z3;z4"/>

<replicatePath> ~ <animatePath> == <animateMotion><mpath></*>


<path id="curve" stroke="black" fill="none" stroke-width="5"
d="M 110,200 C 300,130 400,350 450,150 500,-50 -90,270 110,200" />

<ellipse cx="7" cy="-5" rx="20" ry="14" fill="#aaa" stroke="#666"
stroke-width="2" opacity=".8">
<animateMotion dur="2s" rotate="auto" repeatCount="indefinite" >
<mpath xlink:href="#curve"/>


* - funny
Illustration 7.1
<ellipse cx="300" cy="-300" ry="25" stroke="none" > 
<replicate repeatCount="160">
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="cy"/>
<replicateAttribute attributeName="rx" values="10;60;10" />
<replicateAttribute attributeName="fill" values="rgb(128,45,45);
rgb(128,233,0); rgb(128,233,255);rgb(128,45,45);rgb(128,45,45)"/>
<path id="curve" stroke="black" fill="none" stroke-width="5"
d="M 110,250 C 300,180 400,400 450,200 500,0 -90,320 110,250 z" />
cx and ry values taken from a curve
<ellipse cy="200" ry="25" stroke="none" >
<replicate repeatCount="160">
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="ry"/>
<replicateAttribute attributeName="rx" values="25;45;25" />
<replicateAttribute attributeName="fill"
values="rgb(40,65,120);rgb(255,233,255); rgb(128,233,0);
rgb(128,200,235);rgb(40,65,120);rgb(40,65,120)" />

<path id="curve" stroke="#800" fill="none" stroke-width="5"
d="M 110,145 C 300,75 400,295 450,95 500,-105 -90,200 110,145 z" />
defining three attributes of an object from a single curve
<ellipse cy="200" ry="25" stroke="none" opacity="1">
<replicate repeatCount="260">
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="rx"/>
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="cy"/>
<replicateAttribute attributeName="opacity" values=".6;.5;.3" />
<replicateAttribute attributeName="fill"
rgb(128,200,235);rgb(40,65,120);rgb(140,65,120)" />

<path id="curve"
M 380 282 Q 155 222 306.5 252.5 Q 458 283 241 156.5 Q 24 30 163.5 102 Q 303 174 352.5 186 Q 402 198 418 151.5 Q 434 105 431 148 Q 428 191 461.5 233.5 Q 495 276 464.5 164.5 Q 434 53 473 36 Q 512 19 575.5 41 Q 639 63 657.5 130.5 Q 676 198 679 213.5 Q 682 229 657 284 Q 632 339 623 308.5 Q 614 308 600.5 267.5 Q 587 177 577.5 124 Q 568 71 586.5 206.5 Q 605 302 380 282z
stroke="#800" fill="none" stroke-width="5" />

Two of the use cases built: 

Next: filters. Change the grain of a texture as instances of it recede into the distance: floor tiles, fence posts, railroad ties.


<svgModifier id="f"> 
<modifierChild0 attlist0>
<modifierChild1 attlist1>
<modifierChildn attlistn>

<SVGObject attlistO href>

<replicate repeatCount="100">
[optional list of <replicateAttribute>s and <replicatePath>s]
<replicateModifier modifierType=x childNum=c attributeName=a values="x1;x2;...;xn" />
[optional list of additional <replicateModifier>s]

</ SVGObject >

An svgModifier is considered to be any of <linearGradient>, <radialGradient>, <animate>, <pattern>, <filter>, <clipPath>, and <mask>. Note: our JavaScript parser, thus far deals with only the first three of these.

gradually changing the chroma of one stop of a gradient
<linearGradient id="f" x1="1" y1="0.45" x2="0.15" y2="0.9"> 
<stop offset="0" stop-color="#420" />
<stop offset=".2" stop-color="#f55"/>
<stop offset=".6" stop-color="#fff"/>
<stop offset="1" stop-color="#ff8"/>

<ellipse cy="-1000" cx="350" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >
<replicate repeatCount="100">
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="transform" from="rotate(0 250 200)" to="rotate(50 450 200)" />
<replicateAttribute attributeName="ry" from="100" to="15" />
<replicateAttribute attributeName="rx" from="100" to="10" />
<replicateModifier modifierType="linearGradientStop" childNum="2"
values=" rgb(244,45,45); rgb(233,233,4); rgb(0,0,255);rgb(244,45,45)" />
changing both stops of a gradient
<radialGradient id="f" cx=".8" cy=".6" fx="0" fy=".7"> 
<stop offset=".6" stop-color="#080"/>
<stop offset="1" stop-color="#4f4"/>

<ellipse cy="-1100" cx="200" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >
<replicate repeatCount="90">

<replicateAttribute attributeName="cy" values="300; 150" />
<replicateAttribute attributeName="ry" values="100; 90 ;5" />
<replicateAttribute attributeName="rx" values="100; 150; 140; 10" />

<replicateModifier modifierType="gradientStop" childNum="1"
<replicateModifier modifierType="gradientStop" childNum="0"
values="rgb(244,45,245);rgb(244,45,45); rgb(4,233,4);rgb(30,130,235)"
Illustration 8.3
differntially animating replicates
<linearGradient id="f" x1="0" x2="1" y1="0" y2=".7">
<stop offset=".1" stop-color="#88f"/>
<stop offset=".4" stop-color="#600"/>
<stop offset=".7" stop-color="#b4b"/>
<stop offset="1" stop-color="#ea4" />

<ellipse cy="-1100" cx="200" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >

<animate attributeName="ry" dur="3" repeatCount="indefinite"
values="100; 40; 100"/>
<animate attributeName="rx" dur="3" repeatCount="indefinite"
values="40; 100; 40"/>
<replicate repeatCount="50">
<replicateAttribute attributeName="cy" values="320; 120" />
<replicateAttribute attributeName="cx" values="300; 550;250" />
<replicateModifier modifierType="animate" childnum="0"
attributeName="begin" values="0;8" />

triply nested replicates
<replicate repeatCount="4" >
<replicateAttribute attributeName="transform"
from="scale(1.7), translate(0 0), rotate(0 100 200)"
to="scale(0.9), translate(-100 350), rotate(30 100 200) " />
<g opacity="1">
<replicate repeatCount="7" >
<replicateAttribute attributeName="transform"
from="translate(-50 -50)" to="translate(600 0)" />
<path d="M -10 -10 -20 -20 z" transform="scale(.5)"
stroke="red" stroke-width=".3" stroke-opacity=".2">
<replicate repeatCount="25" >
<replicateAttribute attributeName="d"
from="M 511 63, C 387 201, 320 376, 117 240 C 287 201,
0 76, 511 63z"
to="M 411 73, C 220 151, 190 276, 207 250 C 289 101,
130 126, 411 73z" />
<replicateAttribute attributeName="fill"
values="rgb(45,45,0);rgb(203,100,88);rgb(255,255,200)" />
Illustration 9.2
http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/nestedReplicates4.svg and http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/nestedReplicates5.svg.
replicating across position then across stroke
<ellipse ry="10" cx="330" cy="240" fill="none"> 
<replicate repeatCount="35">
<replicateAttribute attributeName="rx" values="1;320;1" />
<replicateAttribute attributeName="ry" values="20;100;20" />
<replicateAttribute attributeName="transform"
values="rotate(0,330,240);rotate(360,330,240)" />
<replicate repeatCount="6" xmlns="http://granite.sru.edu/svgr">
<replicateAttribute attributeName="stroke-width" values="20;0" />
<replicateAttribute attributeName="stroke"
Illustration 9.4

A gallery: Additional examples and curiosities

The link provided with the abstract at http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/replicate.htm contains most of the examples in this paper and numerous others. A listing of some other noteworthy examples is contained here:



Additional issues and complexities

Source code

  1. already handles above cases plus keySplines and keyTimes
  2. took little time to develop
  3. is  less than 150 lines of code
  4. doesn't yet handle

We have found the <replicate> concept to a powerful one and one which is very "SVG-like." It bootstraps on the concepts of declarative graphics represented both in SVG and in SVG animation. It relies on and extrapolates on many extant parts of the spec. It is, once one begins to play with it, very intuitive for those already well-versed in SVG and SMIL. Developing the Proof of Concept in JavaScript was not at all a painstaking ordeal, suggesting to us that implementation in the wireless and web marketplaces should not be either.

It subtends a lot of the content of previous proposals for <contour> and <doodle>,and adds a heathy range of rich gradients, non-rectilinear textures, and quasi-3D objects to SVG all based on a part of SVG that is familiar to many already!

We are, in short, quite enamored of this technology and hope that you will a) experiment with it and b) advocate for its adoption.


[1] http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html

[2] http://www.inria.fr/recherche/equipes/otto.en.html

[3] http://srufaculty.sru.edu/david.dailey/svg/newstuff/gradient10.svg

[4] http://srufaculty.sru.edu/david.dailey/svg/newstuff/gradientmask1.svg

[5] http://srufaculty.sru.edu/david.dailey/svg/newstuff/gradientfilter1.svg

[6] http://srufaculty.sru.edu/david.dailey/svg/triangles7.svg

[7] http://srufaculty.sru.edu/david.dailey/svg/triangles4l.svg

[8] http://srufaculty.sru.edu/david.dailey/svg/newstuff/tilesA.svg

[9] http://srufaculty.sru.edu/david.dailey/svg/svgopen2008/tiles12.svg

[10] http://srufaculty.sru.edu/david.dailey/svg/gradientfont4.svg (working only in IE/ASV)

[11] Matseevsky, Andrew. Advanced gradient filling. Yahoo groups: svg-developers. March 18,2006. 

[12] Dailey, David, P. The Edges of Plausibility: Exploring the boundaries of filters, animation, gradients, patterns and script in SVG. SVG Open 2008. Nuremberg. https://www.svgopen.org/2008/papers/39-The_Edges_of_Plausibility/

[13] Bousseau, A., Orzan, A., Winnemöller, H., Barla, P., Thollot, J., Salesin, D. 2008. Diffusion Curves: A Vector Representation for Smooth-Shaded Images. ACM Transactions on Graphics (Proc. SIGGRAPH), 27(3), page 92:1 - 92:8.

[14] Israel Eisenberg, Tubefy Explained, Part I. http://owl3d.com/svg/tubefy/articles/article1.html

[15] Dailey, David P. Suggestions for additions to the W3C Specification for SVG October 2008. http://srufaculty.sru.edu/david.dailey/svg/Spec.html

[16] See for example http://www.mathpuzzle.com/tilepent.html or http://srufaculty.sru.edu/david.dailey/tiles/tiling.html

[17] http://srufaculty.sru.edu/david.dailey/svg/svgopen2008/patternTile3.svg

[18] http://en.wikipedia.org/wiki/Turtle_graphics

[19] See http://srufaculty.sru.edu/david.dailey/svg/clipdrag12.svg, http://srufaculty.sru.edu/david.dailey/svg/balloon.svg and http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/barberPole.svg

[20] http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#animation_varieties

[21] Opera has included much of SVG's animation module since initially supporting SVG; IE/ASV has very mature SMIL support, Firefox 4.0 beta has robust SMIL support for most attributes for most of the tags, while Safari and Chrome have fairly rudimentary SMIL implementations as of this writing.

[22] http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#timing

[23] The issues governing "flicker fusion" or the perceptual point at which the the human viewer ceases to perceive the flicker of separate frames of an animation is a complex one governed not just by frames per second but also by the nature of what is happening within those frames. The Wikipedia article provides a general introduction to the topic at http://en.wikipedia.org/wiki/Flicker_fusion_threshold

[24] Some of the examples we use will look a bit more complex because of their use of namespaces. Since <replicate> is not a real part of SVG, we discovered that a bug in the Opera 10.6 browser prevented it from allowing some of the animated examples to start properly. This was overcome with the use of namespace information, by either declaring a namespace preface to the replicate tag: <madeupnamespace:replicate> or by adding a namespace variable within the tag itself: <replicate namespace="madeupnamespace">. One of our authors preferred to just wait until Opera fixes its bug, but the other argued, probably more knowledgeably, that using tags from a foreign name space really should reference that name space in some sense of grammatical compliance with how XML should work. The author with the better sense of humor clearly wrote this footnote

[25] The fact that the retrieved list of SVG tags is actually a node list rather than an array actually makes the code a bit more complex than what is shown here. The example here is merely by way of illustration, for those not familiar with the intricacies of manipulating SVG across browsers with JavaScript.

[26] The source code of the linear gradient itself:

<linearGradient id="f" x1="1" y1="0.45" x2="0.15" y2="0.9"> 
<stop offset="0" stop-color="#ff8"/>
<stop offset=".3" stop-color="#000"/>
<stop offset="1" stop-color="#ff8"/>