Shape Morphing

Since version 1.35, this feature is deprecated and support for it will be dropped in future releases.

Prysm has support for morphing shapes. This allows content creators to animate the shapes of UI elements, opening the doors for effects that transition one shape into another (e.g. a star becomes a circle).

Since standard HTML5 doesn’t support shape morphing, Cohtml has a custom SVG format.

Example morph:

<svg xmlns="" width="800px" height="450px">
    <coherent:morph xmlns:coherent=""

        <coherent:morphing-keyframe percent="60">
            <path fill='blue' stroke='red'
                d=" M 308.05 210.7
                    L 354.7 210.7
                    L 354.7 348.05
                    L 308.05 348.05
                    L 308.05 210.7"/>

        <coherent:morphing-keyframe percent="60.01">
            <path fill='blue' stroke='red'
                d=" M 308.05 210.7
                    Q 326.3 177.0 354.7 210.7
                    L 354.7 348.05
                    L 308.05 348.05
                    L 308.05 210.7"/>

        <coherent:morphing-keyframe percent="100">
            <path fill='orange' stroke='yellow' stroke-width="8" fill-opacity="0.8"
                d=" M 418.05 322.05
                    Q 474.4 322.05 530.7 322.05
                    L 530.7 354.7
                    L 418.05 354.7
                    L 418.05 322.05"/>


    <g id="somename">
        <path fill='orange' stroke-width="5" stroke='yellow'
            d=" M 40.05 48.75
                L 200.7 48.75
                L 200.7 114.75
                L 40.05 114.75
                L 40.05 48.75"/>

The morph is defined by the tag coherent:morph which has coherent:morphing-keyframe elements inside. Only the first keyframe is defined in a special manner, outside of the coherent:morph tag as a <g> element. The reason for this is that all SVG displayers will be able to show the first state of the animation. To match the morph with its first keyframe, the value of the coherent:morph attribute first-frame-group-id must match the id of the <g> containing it.

The animation happens between a pair of keyframes, so the first and second keyframes form an animation, the third and fourth form another and so on. We call each of them an animated pair of keyframes, where the first keyframe is the begin keyframe and the second is the end keyframe. Each animated pair must have one or more SVG path elements, which are animated from the begin to the end keyframe values inside.

For an animation morph to be semantically valid, there are a few rules that must be met:

  1. The total number of keyframes must be an even number (this includes the implicit first keyframe) because we animate between pairs of keyframes
  2. Each begin and end keyframe for an animation pair must have the same number of paths
  3. Each animated path must have the same commands in the begin and end keyframes
  4. The allowed commands inside an animated path are Move, LineTo and QubicBezier
  5. Only path elements are allowed inside a keyframe
  6. Each keyframe must have a specified percent value, similar to CSS animations (the first keyframe which is the <g> element has implicit percent=“0”)

The coherent:morph element has attributes that can specify how the animation is performed, which are equivalent to the CSS ones:

  • animation-duration
  • animation-ease
  • animation-iteration-count
  • animation-delay
  • animation-fill-mode
  • animation-timing-function
  • animation-play-state

The morph element also has a mandatory first-frame-group-id which must match a <g> element with the same id

The coherent:morphing-keyframe element has only one attribute percent corresponding to the CSS percent value of the keyframes declaration. Note that since we interpolate between pairs of keyframes, we cannot interpolate between the second and third keyframes, because they might have a different topology. For this reason, it is advisable to put close percent values as in the example (60 and 60.01). If you leave a larger gap the animation will stay still until the gap time passes, which can be used as an internal animation delay inside the morph if needed.

The SVG can have non-animated parts, mixed with a morph, so you can have a few elements that are not animated and a morphing that animates. The position of the morphing inside the hierarchy is determined by the place where the <g> element is positioned inside the SVG, not the coherent:morph element.

The morph can be placed as background or mask SVG of an element, or as src attribute of a <img> tag. Currently, border SVGs are not supported in general.

The morphing animation can be manipulated through JS in a way similar to the Web Animations API. This can be done through a MorphAnimation JS object which can be acquired by calling:

  • element.getBackgroundSVGAnimation
  • element.getBorderSVGAnimation - currently we don’t have border SVGs
  • element.getMaskSVGAnimation
  • element.getSrcSVGAnimation - if the element is an image

For Web Animations API reference, check the JS documentation.