Clients
What a client does with an animated avatar
The network carries the picture once; what your contacts see afterwards is decided in the client, not in the protocol.
An XMPP avatar is a small image attached to an account, published once and fetched by whoever needs it. Whether it animates on a contact’s screen is not decided by the network or by the file alone: it is decided by the client that renders it. Two people can look at the same account and see a still frame on one device and a moving loop on another, and both are behaving correctly. The difference lives in how each client decodes and displays the image it was handed.
The publication side is the simple part. The vCard-based mechanism in XEP-0153 stores a photo alongside the account’s other metadata; the newer XEP-0084 publishes avatars over publish-subscribe as a data node with a metadata node alongside it, so clients can announce a change without forcing every contact to re-download. The extension document itself is explicit that the payload is opaque: the network moves bytes, and the format of those bytes is a matter between the publisher and the renderer.
Rendering is where it gets interesting, because an animated GIF is not one image but a sequence of frames with timings and disposal rules, and a client that treats it as one image shows only the first frame. Developers building or patching a Java client run into this quickly: the standard image stack reads a GIF’s header happily and stops there, so the animation has to be decoded frame by frame and re-timed by hand. The technical guide at animated GIFs in Java walks that machinery, assembly of frames into a loop, delays, disposal methods, colour tables, in enough detail to show why most clients choose the still.
What does the client actually receive?
Whatever was published, usually capped. Both avatar mechanisms expect the publisher to keep the image small, and several servers enforce a ceiling on payload size before the client sees anything. In practice the dominant formats are PNG, JPEG and GIF, and a conscientious publisher resizes before upload rather than relying on the server to object. A contact list of two hundred accounts, each carrying a one-megabyte avatar, is a polite way to make a mobile client miserable.
Two delivery paths matter. Under the vCard route, the image is part of the account’s profile document, fetched on demand and cached. Under XEP-0084, the client subscribes to the avatar metadata node and receives a notification when the image changes, then pulls the data node only if it wants the new picture. The second path is kinder to bandwidth and kinder to battery, which is why the mobile essay cares about it: a phone on a metered connection wants to know about a change without being made to download it.
Why does animation split the room?
Because stillness is the cheap correct answer and motion costs effort three times over. First, decoding: a looping GIF has to be parsed as a sequence, and the toolkit bundled with the platform may not do that. Second, timing: frames carry their own delays, and a renderer that ignores them plays the loop at the wrong speed or not at all. Third, policy: an animated avatar redraws itself forever in a roster of hundreds, which is GPU and battery spent on decoration, and a fair number of clients deliberately render the first frame and stop.
The result is a quiet interoperability note that the specifications never needed to settle. XEP-0084 describes a recommended thumbnail of 64 by 64 pixels precisely so that a client has a still it can rely on; the full-size image is allowed to be richer, including animated, because the thumbnail exists as a fallback. A client that shows only thumbnails is not broken, and a client that plays the animation is not more correct. They are reading the same publication at different depths.
The Java angle
The Java ecosystem turns up twice in this story. Openfire is written in Java, and Spark, its sibling client, is too, so the question of how Java handles animated images is not academic for a slice of the network. Java’s ImageIO reads GIF files and will hand back every frame with its metadata, but it will not play them: the timing and disposal logic is the caller’s problem, which is why a small library or a careful loop of BufferedImage frames tends to appear in client codebases that want motion. Getting the disposal methods wrong produces the classic artefact where old frames ghost through new ones.
What to check before blaming the protocol
When an avatar looks wrong, the checklist is short. Is the image published at all, and on which mechanism, vCard or XEP-0084? Does it exceed a size limit the server or the receiving client imposes? Is the format one the receiving client can decode, and if it is animated, does that client render animation at all? The protocol’s job ends at delivery of well-formed bytes; everything after that is the kind of feature difference between clients that this layer keeps coming back to. An avatar that moves for one contact and not another is rarely a bug in the network. It is two clients making two reasonable choices.