Proposals/Variable width stroke

From SVG
< Proposals
Revision as of 07:24, 18 September 2013 by Bbirtles (Talk | contribs)

Jump to: navigation, search

Syntax proposal


 stroke-widths-values: [<percentage> | <length>]#
 stroke-widths-positions: [<percentage | <length>]#
 stroke-widths-repeat: no-repeat | repeat (Initial: no-repeat; future value: reflect?)
 stroke-widths: [<value> <position>?]# <repeat>?

Values list:

  • <percentage> values represent percentages of the computed value of the stroke-width property.

Positions list:

  • <percentage> values represent percentages of the path length.
  • Add a 'seg' length unit that is a floating-point number where the integer part is a zero-based index into the segments in the context path (e.g. 0 represents the segment created by the first path command that is not a moveto command) and the fractional part is an offset into the segment based on the length of the segment. e.g. 3.5 represents the mid-point of the fourth drawn segment.
    • Issue: should moveto segments be considered segments? Is there a precedent for this elsewhere?
    • Issue: should the fractional part be based on the path length or the t value?
  • If there are more values than positions? → as with CSS gradients, set position of last value to 100% and space other values out (using percentages of the path length)
    • See issue under use case (d). We might define this differently depending on the last value in the list of positions.
  • If there are more positions than values? → ignore extra positions
  • If there are gaps in the list of positions (when using the short-hand)? → as with CSS gradients, evenly space out values (based on percentages of the path length)
  • If the list of positions is not in ascending order? → as with CSS gradients, clamp each value to max(<prev-position>, <position>)
  • Range of percentage positions? → as with CSS gradients, values < 0% and > 100% are permitted


  • no-repeat means that if the final position is less than 100%, the stroke-width remains fixed at the final stroke width value for the remainder of the path.
  • repeat means that if the final position is less than 100%:
    • If the list of values is entirely segment lengths then you repeat the list by adding the position values to the last segment value (e.g. 0seg, 1seg, 2seg0seg, 1seg, 2seg, 2seg, 3seg, 4seg…; 1seg, 2.5seg, 5seg1seg, 2.5seg, 5seg, 6seg, 7.5seg, 10seg, 11seg…)
      • Issue: It may be more intuitive in some cases that 0seg, 1seg, 2seg becomes 0seg, 1seg, 2seg, 3seg, 4seg, 5seg.
    • Otherwise, take the computed(?) value of the positions and repeat that by adding them onto the final position value.

Asymmetric variant

stroke-widths-values: [ [<percentage> | <length>] [ /[<percentage> | <length>] ]? ]#

The two numbers represent the width on the left-side of the stroke (/) and the right-side of the stroke respectively. If there is only one number it is symmetric.

  • My current suggestion is allow asymmetric stroke widths from the start but not negative stroke widths. This happens to coincide with Illustrator's feature set and I think most of the complexity comes with negative stroke widths, not asymmetric stroke widths.

Syntax examples

a) A hair stroke pattern

 .hair {
   stroke-width: 10px;
   stroke-widths-values: 10%, 110%, 30%;
   stroke-widths-positions: 0, 70%;
   stroke-linecap: round;

Or with the shorthand:

 .hair {
   stroke-width: 10px;
   stroke-widths: 10% 0, 110% 70%, 30%;
   stroke-linecap: round;

b) A bulging pattern like a snake that has eaten a lot of eggs.

 .snake {
   stroke-width: 30px;
   stroke-widths: 50% 30px, 100% 60px repeat;

c) An n-pointed star where the outside points and inside points differ in stroke width {
   stroke-widths: 150% 1seg, 100% 2seg segment repeat;

d) A path created using touch input where the stroke widths correspond to touch pressure

 <path d="M123 345C...." stroke-widths="1 0seg, 1.1 1seg, 1.15 2seg, 0.8 3seg, ..." />

(Issue: We have discussed adding a special mode where every value aligns with a segment. Could we define the position list interpolation so that if the last value in the list of positions is a 'seg' value then fill out the positions list by just adding '1seg' to for each value? That would make the above:)

 <path d="M123 345C...." stroke-widths-positions="0seg" stroke-widths-values="1, 1.1, 1.15, 0.8, ..."/>



  • Will there be a proper fallback: a specific feature name usable in svg:switch?
  • How will variable stroke-widths interact with vector effects?

(Could it actually be implemented as a vector effect so that if problems are found a new vector effect could be created without invalidating the original one?)

  • How will stroke position work with this? (There is high demand for adjustable stroke positioning. If we have asymmetric widths you can be sure it will be used to simulate this. Maybe we should just bite the bullet spec stroke position now.)
  • How are dashes handled (e.g. dash caps)?
  • Can other properties depend on path position, e.g. color?
  • How will line joins be handled with repeated patterns where the beginning and ending widths are different and the path segments are at an angle?


  • Inkscape's Power Stroke implementation:

I was slightly wrong about how Inkscape determines the position along a path. The fractional part is actually the 'time value' of the Bezier curve as was easier to use that the actual distance. Thus:

 unit part -> node index
 fractional part -> 'time value'
  • Mixing linear and smooth parts:

Could be done by adding a smoothness type for each point. Inkscape's Spiro LPE uses the following notation:

 c 0, 10px --> curved
 c 1, 20px
 ] 2, 10px --> left of node curved, linear on right
 s 2.5, 15px --> linear
 [ 3, 10px --> right of node curved, linear on left
 c 4, 20px
 c 5, 10px

Synfig uses a smoothness value the interpolates between a smoothed curve and a straight line.

  • Positioning using percentage is more computationally intensive if only part of path shown.

Other implementations of variable stroke width:

  • Synfig:

Positioning along length either:

 Homogeneous: based on percentage of length.
 Non Homogeneous: based on percentage of points (a three Bezier path has four points so 0.5 means middle of second Bezier).
 Width is percentage of nominal stroke width, negative values allowed.
 Smoothing done by 5th degree spline smooth function in linear combination with linear interpolation.
 A smoothness parameter allows change between smooth and linear. See:

End caps are discontinuous when smoothness is zero and continuous when smoothness is maximum.

With dashed lines, allows custom caps and a variable width within dash.

Not yet in Wiki: Complex method to keep width control points at same point along path when extending a path (needed when several hand-drawn strokes are merged into one path). (I think Inkscape's method deals with this better.)

  • Graffiti markup:

Includes parameters for speed, drip, rotation, etc.