The <spine-skeleton> tag allows you to embed your Spine animations into a web page as a web component.

By default, the animation bounds are calculated using the specified animation, or the setup pose if no animation is provided.
The bounds are centered and scaled to fit the parent container.

                
            
You can change the fit mode of your Spine animation using the fit attribute.

This is fit="fill". The default fit value is fit="contain".
If you want to preserve the original scale, you can use fit="none". In combination with that, you can use the scale attribute to set your desired scale.

Other fit modes are width, height, cover, and scaleDown.

                
            
If you want to manually size the Spine widget, specify the width and height attributes in pixels (without the "px" unit).

If you prefer, you can style the component using the style attribute, which provides more styling options.

                
            
The origin mode centers the animation's world origin with the center of the HTML element.
You are responsible for scaling the skeleton when using this mode.

Move the origin by a percentage of the div's width and height using the x-axis and y-axis attributes, respectively.

                
            
Use offset-x and offset-y to move your skeleton left or right by the specified number of pixels.

                
            
You can virtually add padding to the element container using pad-left, pad-right, pad-top, and pad-bottom.

For the values, you can use a percentage of the width for left and right, and a percentage of the height for top and bottom.

                
            
You can customize the bounds, for example to focus on specific details of your animation.

The bounds-x, bounds-y, bounds-width, and bounds-height attributes allow you to define custom bounds.

In this example, we're zooming in on Celeste's face. You’ll probably want to use clip in this case to prevent the skeleton from overflowing.

                
            
Assign an identifier to your widget to retrieve it using the spine.getSpineWidget function. You can easily access the Skeleton and AnimationState of your character, and use them as if you were working with spine-webgl.

If you change the animation, you can ask the widget to rescale the skeleton based on the new animation. See the code below.

                
            

To change the animation, you can simply modify the animation attribute. The widget will reinitialize itself and switch to the new animation.

In this case, you should use auto-calculate-bounds to have the widget always recalculate the bounds, as shown in the top example.

If you want to keep the scale consistent while fitting multiple animations in the container, you can use the animation-bounds attribute to define bounds that include a list of animations, as shown in the bottom example.


                
            

If you want to display a sequence of animations without using JavaScript, you can use the animations attribute.

It accepts a string composed of groups enclosed in square brackets, like this: [...][...][...]

Each group represents an animation to play, with parameters in a comma-separated list:

  1. track: the track number to play the animation on
  2. animation name: the name of the animation
  3. loop: true to loop the animation, false otherwise
  4. delay: the number of seconds to wait after the previous animation starts (not used for the first animation on a track)
  5. mixDuration: the mix duration between this animation and the previous one (not used for the first animation on a track)

To loop a track once it reaches the end, add the special group [loop, trackNumber], where:

  • loop: identifies this as a loop instruction
  • trackNumber: the number of the track to loop

The parameters of the first group on each track are passed to the setAnimation method, while the remaining groups use addAnimation.

To use setEmptyAnimation or addEmptyAnimation, use #EMPTY# as the animation name. In this case, the loop parameter is ignored.

The default-mix attribute allows you to set the default mix duration for the AnimationState.

See the two examples below.

Spineboy uses the following value for the animations attribute:

We use a single track for this animation. Let's break it down:
  1. [loop, 0]: when track 0 reaches the end, it loops back to the beginning
  2. [0, idle, true]: sets the idle animation to loop
  3. [0, run, false, 2, 0.25]: queues the run animation, starts it after 2 seconds with a 0.25-second mix
  4. [0, run, false]: queues another run animation
  5. [0, run, false]: queues another run animation
  6. [0, run-to-idle, false, 0, 0.15]: queues run-to-idle with no delay and a 0.15-second mix
  7. [0, idle, true]: queues the idle animation to loop
  8. [0, jump, false, 0, 0.15]: queues jump with no delay and a 0.15-second mix
  9. [0, walk, false, 0, 0.05]: queues walk with no delay and a 0.05-second mix
  10. [0, death, false, 0, 0.05]: queues death with no delay and a 0.05-second mix

Celeste uses the following value for the animations attribute:

This example uses two tracks. Track 0 plays the wings-and-feet animation.
Track 1 loops, playing an empty animation followed by an eyeblink animation with a 2-second delay.

You can modify the textarea above and experiment. For example, change the delay from 2 to 0.5, or add the swing animation to track 0 like this: [0, swing, true, 5, 0.5] to start it after 5 seconds with a 0.5-second mix. Click the button below and Celeste will start blinking more frequently.


                
            
Moving the div changes the position of the skeleton's origin.
Resizing the div will scale the skeleton when in inside mode, but not when in origin mode.
Try dragging and resizing the div in the example above to see the effect.

                
            
You can view the skeleton's world origin (green), the root bone position (red), and the bounds rectangle and center (blue) by setting debug to true.
Here we slightly shift the root to prevent it from overlapping with the origin.

                
            
If you remove a widget from the DOM, it won't be disposed because you might want to append it to another node. If you want to actually dispose of a widget, call dispose() on it. dispose() is safe and won't release resources if they're still used by other widgets.
If you want to dispose of everything, call dispose() on the overlay.

The following example allows you to create and dispose of widgets using the same assets. Below, you can see the number of references to each asset. A red message will appear when an asset is actually disposed. If the number of skeleton references is higher than expected, it's because other widgets on the page are still using it.

Widgets: ?
Skeleton references: ?
Atlas references: ? DISPOSED
Texture references: ? DISPOSED

                
            
If you want to embed your assets within the page, you can inline them using their base64 versions. Use a stringified JSON object where the keys are the asset names and the values are their base64-encoded contents.

                
            
A loading spinner is shown while assets are loading. Click the button below to simulate a 2-second loading delay:



If you don't want to show the loading spinner, set no-spinner.
Click the button below to toggle the spinner.


                
            
It's very easy to display your different skins and animations. Simply create a table and use the skin and animation attributes.

                
            
If you have multiple atlas pages, for example one for each skin, and you want to display only some of the skins, pass the atlas pages you want to load to the pages attribute as a comma-separated list of indices.

                
            
Let's do the same thing above, but programmatically! Create two arrays, one for the skins and the other for the animations, and loop through them.

spine.createSpineWidget allows you to create a Spine widget.

By default, assets are loaded immediately. You can delay this by setting manual-start="false". Then, add the widget to the DOM using the asynchronous method appendTo. It is your responsibility to call start() on the widget. As usual, just wait for the whenReady to interact with the skeleton or the state.

                
            
When the widget (or the parent element) enters the viewport, the callback onScreenFunction is invoked.

By default, the callback does two things:
  • sets onScreenAtLeastOnce to true when the widget enters the viewport for the first time
  • if start-when-visible is set, the widget's start method is invoked the first time the widget enters the viewport, and the assets are loaded at that moment.

The assets of the coin below are loaded only when the widget enters the viewport.

You can overwrite the onScreenFunction behavior. For example, the raptor below changes its animation every time the widget enters the viewport.

                
            
If you want to load textures programmatically, you can just pass an empty value for pages like this pages="".

In this way, the skeleton and the atlas are loaded, but not the textures.
Then you can load the textures whenever you want.










                
            
Widgets are not rendered while they are off-screen.

The state and skeleton update, and the skeleton apply and the skeleton updateWorldTransform functions are not invoked when the widget is off-screen.

If you want the update functions to be invoked in any case, set offscreen=update.
If you want all the functions to be invoked in any case, set offscreen=pose.

You can also overwrite the update function. Just assign a function to the update property of the widget. In that case, it's your responsibility to skip the update/apply. You can use the onScreen property for convenience.

                
            
If for some reason your skeleton bounds go outside the div, you can use the clip property to clip everything that is outside the HTML container.

Be aware that this will break batching!

                
            
More examples for clip attribute.

                
            
If you use a spine widget in an element that has an ancestor that does not follow the webpage scroll, the effect might not be the desired one. You might encounter these problems:

1) For scrollable containers, the widget will be slightly slower to scroll than the HTML behind. The effect is more evident for lower refresh rate displays.
2) For scrollable containers, the widget will overflow the container bounds until the widget HTML element container is visible.
3) For fixed containers, the widget will scroll in a jerky way.

In order to fix this behavior, it is necessary to insert a dedicated spine-overlay web component as a direct child of the container. Moreover, it is necessary to perform the following actions:

1) The scrollable container must have a transform CSS attribute. If it doesn't have this attribute, the spine-overlay will add it for you. If you don't want this attribute to be added, set the no-auto-parent-transform on the spine-overlay. But watch out, the widget might not work as intended.
2) The spine-overlay must have an overlay-id attribute. Choose the value you prefer.
3) Each spine-skeleton must have an overlay-id attribute. The same as the hosting spine-overlay.

Additionally, you can set overflow-top, overflow-bottom, overflow-left, overflow-right attributes to the spine-overlay in order to make the canvas bigger and prevent scrolling artifacts.

See the two examples below:
- Click the following button to open two elements with fixed positioning. The left one does not have a dedicated overlay and will scroll in a jerky way.
- Below there are two scrolling items. The left one does not have a dedicated overlay, it will lag on scroll and the widgets will overflow the container.


                
            
As a bonus item, you can move your skeleton around just by setting the isdraggable property to true.

                
            
If you need to determine the cursor position in the overlay world, you might find useful the following properties.
For spine-skeleton: For spine-overlay: You can use these properties to interact with your widget. See the following examples where the owl eyes will follow the cursor, even if you drag the owls to another position. Exaggerate the movement by deselecting the checkbox below.



This feature is experimental and might be removed in the future.

                
            
You can attach callbacks to your widget to react to pointer interactions. Just make it isinteractive.

You can attach a callback for interactions with the widget's bounds or with slots. The available events are down, up, enter, leave, move, and drag.

In the following example, if the cursor enters the bounds, the jump animation is set, while the wave animation is set when the cursor leaves.
If you click on the head-base slot (the face), you can change the normal and dark tint with the colors selected in the two following selectors.

Tint normal:

Tint black:


                
            
You can make your HTMLElements follow slots. This feature is convenient when you need to generate dynamic text or content that integrates with your animation.

Invoke the `followSlot` function that takes as input:

  1. The Slot or the slot name to follow
  2. The HTMLElement that follows the slot
  3. An object with the following options:
    • followOpacity: the element opacity is connected to the slot alpha
    • followScale: the element scale is connected to the slot scale
    • followRotation: the element rotation is connected to the slot rotation
    • followAttachmentAttach: the element is shown/hidden depending if the slot contains an attachment or not
    • hideAttachment: the slot attachment is hidden as if the element replaced the attachment


                
            
`followSlot` works even with other spine widgets! It works even if you drag it :D