Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC2545: Image Packs (Emoticons & Stickers) #2545

Open
wants to merge 36 commits into
base: old_master
Choose a base branch
from
Open
Changes from 28 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
bc55ff6
add emotes proposal
Sorunome May 15, 2020
0f4bd5e
add more stuffs
Sorunome May 15, 2020
2d191f6
fix naming thingy
Sorunome May 15, 2020
fa3a8be
fix typo
Sorunome May 15, 2020
96f2864
outline room emote packs and emote rooms
Sorunome May 20, 2020
81ccecb
remove comma
Sorunome May 20, 2020
dc43e79
address things
Sorunome May 20, 2020
4435d75
update emotes msc
Sorunome Jun 20, 2020
79aae8a
update
Sorunome Jun 20, 2020
4fea69b
Fix typos and grammar mistakes
Sorunome Jun 24, 2020
0e382e0
m.emote --> m.emoticon, update dedupe and pack priority
Sorunome Sep 1, 2020
72b2174
update stuff
Sorunome Sep 24, 2020
2e2987b
add a paragraph over emojis and stuff
Sorunome Sep 30, 2020
e8c2e34
Re-word proposal to sound more firm, address lots of suggestions
Sorunome Oct 25, 2020
b41c091
specify that emotes can be animated
Sorunome Oct 25, 2020
4f6d147
update msc to also include stickers and thelike
Sorunome Mar 25, 2021
5351cb3
update a few things
Sorunome Mar 25, 2021
12c8fa8
add a few more words on slugs
Sorunome Mar 25, 2021
e8d910f
Update proposals/2545-emotes.md
Sorunome Mar 26, 2021
a879bcd
license --> attribution
Sorunome Jul 4, 2021
580a836
Update proposals/2545-emotes.md
Sorunome Jul 18, 2021
43f20ad
Merge branch 'soru/emotes' of github.com:Sorunome/matrix-doc into sor…
Sorunome Jul 18, 2021
739b4c0
Update with "looking further" section and slightly update comparison
Sorunome Jul 18, 2021
51bcec2
add a sentance to suggest image packs from spaces
Sorunome Jul 24, 2021
71a3c55
soru bamboozled herself!
Sorunome Jul 24, 2021
a7a4a36
Fix small wording and ensure that emotes from canonical spaces are to…
Sorunome Sep 16, 2021
be7d0df
Update spelling mistakes and wording, only suggest deduplication meth…
Sorunome Oct 28, 2022
b5ca351
fix some typos
anoadragon453 Sep 19, 2023
3517a16
Make reference to spec/client naming disparity timeless
anoadragon453 Aug 15, 2024
5d62d52
Require `alt` and `title` attributes for emotes
anoadragon453 Aug 15, 2024
e5b9c2c
Link `m.sticker` spec; small wording changes
anoadragon453 Aug 15, 2024
44c5ed3
Prevent clients from rendering emotes behind non-MXC uri schemes
anoadragon453 Aug 15, 2024
ea50cc7
Clarify client requirements around emote resolution
anoadragon453 Aug 15, 2024
3c2e29c
Add a new proposal intro
anoadragon453 Aug 15, 2024
c429552
Recommend default of 32px for emote height
anoadragon453 Aug 28, 2024
d4e9cd2
Specify image formats are equivalent to m.image
anoadragon453 Aug 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 269 additions & 0 deletions proposals/2545-emotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# MSC2545: Image Packs (Emoticons & Stickers)
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this MSC will intersect with MSC3911, I think we should consider this blocked until MSC3911 (or some other solution to the issues solved by that MSC) is merged.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this has to be blocked on MSC3911 - MSC2545 is already in use in the wild all over the place. MSC3911 will need to figure out how to address it, but not block the whole of this on media linking/auth.


Emoticons, or short emotes...we need them!
Sorunome marked this conversation as resolved.
Show resolved Hide resolved

We also need proper stickers, too!
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

## Terminology
### Emoticons
Since there is a lot of confusion of how this relates to `m.emote`, why this isn't called "custom emoji"
etc,:

`m.emote` is for emotion - and it has been incorrectly named this way. `m.action` would have been more
appropriate, as you use it to describe *actions*, not *emotions*. E.g. "/me is walking to the gym", or
"/me is happy" and *not* "/me happy".

That, however, is *not* what this MSC is about. Instead, it is about emoticons, also known in short as
emotes.

Emoticons are just little images or text describing emotions or other things. Emoji are a subset of
emoticons, namely those found within unicode. Custom emoji here would actually refer to a custom emoji
font, that is your own rendering of 🦊, 🐱, etc., *not* new images. New images is what custom emoticons
are for.
Sorunome marked this conversation as resolved.
Show resolved Hide resolved

Now, a client may choose to name these however they like. We already have a naming disparity between
spec and clients with groups vs communities. It is, however, imperative to name things in the spec
accurately after what they are.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

### Stickers
Stickers already exist in Matrix. They are reusable images one can send, usually as a reaction to
something sent in the timeline. This MSC adds a way to distribute and define a source for a client to
send them.

## Proposal
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
### Emoticons in the formatted body
Emoticons have at least a shortcode and a mxc uri. They are sent as `<img>` tags, which are currently in
the spec. As such, many existing clients are able to render them.
To allow clients to distinguish emoticons from other inline images, a new
property, `data-mx-emoticon`, is introduced. A client can choose to ignore the size attributes of emoticons
when rendering, and instead pick the size based on other circumstances. This could e.g. be used to
display messages with only emoticons and emoji as larger than usual, which is commonly found in
messengers. Such an `<img>` tag of a shortcode `emote` and a mxc uri `mxc://example.org/emote`
could look as follows:

```html
<img data-mx-emoticon src="mxc://example.org/emote" alt=":emote:" title=":emote:" height="32" />
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
```

Both the `alt` and the `title` attributes are specified as they serve different purposes: `alt` is
displayed if e.g. the emote does not load. `title` is displayed e.g. on mouse hover over the emote.
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
Thus, specifying both the `alt` and `title` attributes is required.
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

The `height` is just a height that looks good on most devices with the normal, default font size.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
No width is displayed as to not weirdly squish non-square emotes. In order to maintain backwards-compatibility
with clients not supporting emotes, specifying the `height` is required.

If the new `data-mx-emoticon` attribute has a value, it is ignored. Due to limitations of some libraries
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
the attribute may even look like `data-mx-emoticon=""`.

The `src` attribute *must* be a mxc url. Other URIs, such as `https`, `mailto` etc. are not allowed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `src` attribute *must* be a mxc url. Other URIs, such as `https`, `mailto` etc. are not allowed.
The `src` attribute *must* be a mxc uri. Other URIs, such as `https`, `mailto` etc. are not allowed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, we need consequences listed: does the event not get rendered, or just the emoticon?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've applied the suggestion in the first comment in a separate commit.

We need consequences listed: does the event not get rendered, or just the emoticon?

I don't see why the rest of the event should not be rendered. It'd be ideal to still be able to see the text in a message, even if the emotes aren't formatted properly (and are thus not rendered). Do you agree?


### Sending stickers
To send stickers, the already spec'd `m.sticker` is used.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

### Image types
Emoticons are recommended to have a size of about 128x128 pixels. Even though the fallback specifies
a height of 32, this is to ensure that the emotes still look good on higher DPI screens.

Stickers are recommended to have a size of up to 512x512 pixels.
Sorunome marked this conversation as resolved.
Show resolved Hide resolved

Furthermore, these images should either have a mimetype of `image/png`, `image/gif` or `image/webp`.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
They can be animated.

Due to the low resolution of emotes, `image/jpg` and `image/jpeg` have been purposefully excluded from this
list.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

### Image pack event
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
The image pack event has a type of `m.image_pack`. It contains a key `images`, which is a map from a
short code to an image object. It may also contain a key `pack`, which is a pack object, described in
the following section.

#### Pack object
The pack object consists of the following keys:
- `display_name`: (String, optional) A display name for the pack. Defaults to the room name, if the
image pack event is in the room. This does not have to be unique from other packs in a room.
- `avatar_url`: (String, optional) The mxc uri of an avatar/icon to display for the pack. Defaults
to the room avatar, if the pack is in the room. Otherwise, the pack does not have an avatar.
- `usage`: (String[], optional) An array of the usages for this pack. Possible usages are `"emoticon"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's worth allowing implementations to create their own usages, in which case, we probably want the usages to be m.emoticon and m.sticker, and have the custom usages follow the Java package naming convention as usual.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the usual spec process of being able to add unstable prefixes to values in this field is sufficient? The current wording doesn't prevent that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. It just seems to me like naming it m.emoticon instead of just emoticon makes it easier and clearer. Anyways, it's a non-blocking comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has anyone seen any use cases for additional "usages" in the wild while this MSC has been implemented? @deepbluev7 perhaps?

If not, perhaps it's just best to leave this as is.

and `"sticker"`. If the usage is absent or empty, a usage for all possible usage types is to be assumed.
- `attribution`: (String, optional) The attribution of this pack.
Sorunome marked this conversation as resolved.
Show resolved Hide resolved

Example:
```json
{
"display_name": "Awesome Pack",
"usage": ["emoticon"]
}
```

#### Image object
The image object consists of the following keys:
- `url`: (String, required) The mxc URL for this image.
- `body`: (String, optional) An optional text body for this image. Useful for the sticker body text or
the emote alt text. Defaults to the shortcode.
- `info`: (`ImageInfo`, optional) The already spec'd `ImageInfo` object used for the `info` block of
`m.sticker` events.
- `usage`: (String[], optional) An array of the usages for this image. The possible values match those
of the `usage` key of a pack object. If present and non-empty, this overrides the usage defined at
pack level for this particular image. This is useful to e.g. have one pack contain mixed emotes and
stickers. Additionally, as there is only a single account data level image pack, this is required to
have a mixture of emotes and stickers available in account data.

#### Example image pack event
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
Taking all of this into account, an example pack event may look like the following:
```json
{
"images": {
"myemote": {
"url": "mxc://example.org/blah"
},
"mysticker": {
"url": "mxc://example.org/sticker",
"usage": ["sticker"]
}
},
"pack": {
"display_name": "Awesome Pack",
"usage": ["emoticon"]
}
}
```

### Image sources
There are several places where a client is expected to look for events with type `m.image_pack`, mainly
in their own account data and in room state.

#### User image packs
Each user can have their own personal image pack defined in their account data, under the
`m.image_pack` key. The user is expected to be presented with these images in all rooms.

#### Room image packs
A room can have an unlimited amount of image packs, by specifying the `m.image_pack` state event with
different state keys. By default, the user is expected to be presented with these images only in the room they
they are defined in. To enable them to be presented in all rooms, see the "Image pack rooms" section.
E.g. a discord bridge could set as state key
`de.sorunome.mx-puppet-bridge.discord` and have all the bridged emotes in said state event, keeping
bridged emotes from matrix emotes separate.

#### Space image packs
Clients should suggest image packs of a room's canonical space, if the user is also in that space.
This should be done recursively on canonical spaces. So, if a room has a canonical space and
that space again has a canonical space, the clients should suggest image packs of both of those spaces.

anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
#### Image pack rooms
While room image packs are specific to a room, they can be made accessible from anywhere by setting
the `m.image_pack.rooms` key in a user's account data. The value is an object, with a `room` key containing
a map of room ids to the state keys to an object. If a room id / state key combination is provided in this form,
clients should make the corresponding room image pack globally accessible in all rooms.

Note that while this MSC does not define any keys for the bottom-level object, defining it as an object means greater
flexibility in the case of future changes.

The contents of `m.image_pack.rooms` could look like the following:

```json
{
"rooms": {
"!someroom:example.org": {
"": {},
"de.sorunome.mx-puppet-bridge.discord": {}
},
"!someotherroom:example.org": {
"": {}
}
}
}
```

Here three emote packs are globally accessible to the user: Two defined in `!someroom:example.org`
(one with blank state key and one with state key `de.sorunome.mx-puppet-bridge.discord`) and one in
`!someotherroom:example.org` (with a blank state key).

### Image pack source priority and deduplicating
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
If a client gives image suggestions (emotes, stickers) to the user in some ordered fashion (e.g. an
ordered list where you click an entry), the order of the images should be predictable between rooms.
A suggestion for clients of image pack ordering is as follows:
1. User image pack (defined in your own account data)
2. Image pack rooms (defined in the `m.image_pack.rooms` account data object)
3. Space image packs (defined in the hierarchy of canonical spaces for the current room)
4. Room image packs (defined in the currently open room's state)

Furthermore, clients are expected to de-duplicate images so that same images are not displayed multiple
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that de-duplicating images is the best UI. If there is an image that appears in two image packs, and I'm used to picking that image from pack 1, but the client only displays it in pack 2, then I won't be able to find the image that I want.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also makes it more difficult to manage packs, in case you want to manually remove duplicates.

Perhaps it would make more sense to only de-duplicate packs themselves?

Copy link

@Gremious Gremious Aug 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, less relevant and only marginally importnat, but assuming you can click on a sticker to view it's name in the chat like you can with discord stickers/emojis, that should probably be taken into account.

So if you have a smug/grimacing face in one pack titled "I'm gonna get u" and the same sticker in another pack titled "satisfied", you could potentially want to send the different stickers in different rooms, on the assumption that people are gonna click and read the name. And you'll probably search the sticker by the name you intend to convey.

Whether people actually use emojis/stickers like that is up to debate, but hey, I like naming my emoji nicely in discord, so I can kind of see it.


If de-duplicating is "automatic", then don't know if it's really even necessary. I don't see why a user shouldn't be able to just put 400 of the same sticker in their own pack if they wanted to. They probably won't anyway, buf if they do it as a joke, who cares. Feels like slightly wasted development time to me to put an arbitrary restriction.

A button to remove duplicates in the pack on demand sounds cool though, makes life easier if you just have a big sticker folder that might have a few repeating images, e.g. from downloading/collecting stickers you see manually.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A button to remove duplicates in the pack on demand sounds cool though, makes life easier if you just have a big sticker folder that might have a few repeating images, e.g. from downloading/collecting stickers you see manually.

Good idea, though this can be implemented in the client. The current spec won't get in the way of such a client feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deepbluev7 What are your thoughts on this, given your experience with implementing this MSC?

times. Such scenarios include:
1. when viewing a room that has a pack defined in the `m.image_pack.rooms` account data object, and
2. a bot which syncs emotes over multiple rooms.

This could be done by deduplicating by the mxc URI.

### Sending
#### Emoticons
For emoticons a client could add delimiters (e.g. `:`) around the image shortcode, meaning
that if an image has a shortcode of `emote`, the user can enter `:emote:` to send it. If there are
multiple emoticons with the same shortcode in a room, the client could e.g. slugify the packs display
name and then have the user enter `:slug~emote:`. As slugs typically match `^[\w-]+$`, that should
ensure complete-ability.

The alt / title text for the `<img>` tag is expected to be the `body` of the emote. If absent, its
shortcode should be used instead.

Sorunome marked this conversation as resolved.
Show resolved Hide resolved
#### Stickers
When sending a sticker, the `body` of the `m.sticker` event should be set to the `body` defined for that
image, or if absent, its shortcode.

The `info` object of the `m.sticker` event should be set to the `info` object of the image, or if absent,
an empty object.

Sorunome marked this conversation as resolved.
Show resolved Hide resolved
## Security Considerations
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
When sending an `<img>` tag in an encrypted room, the client will make a request to fetch
said image, in this case an emote. As there is no way to encrypt content behind `<img>` tags yet,
Sorunome marked this conversation as resolved.
Show resolved Hide resolved
this could potentially leak part of the conversation. This is **not** a new security consideration,
it already exists. This, however, isn't much different from posting someone a link in an e2ee chat and
the recipient opens the link. Additionally, images, and thus emotes, are often cached by the client,
not even necessarily leading to a http query.

Related issue: https://github.com/matrix-org/matrix-doc/issues/2418

## Unstable prefix
The `m.image_pack` in the account data is replaced with `im.ponies.user_emotes`. The `m.image_pack` in
the room state is replaced with `im.ponies.room_emotes`. The `m.image_pack.rooms` is replaced with
`im.ponies.emote_rooms`.

Some existing implementations using `im.ponies.user_emotes` and `im.ponies.room_emotes` currently use
a dict called `short` which is just a map of the shortcode to the mxc url.

Some other implementations currently also call the `images` key `emoticons`.

## Alternatives
One can easily think of near infinite ways to implement emotes. The aspect of sending an `<img>` tag
and marking it as an emote with `data-mx-emoticon` seems to be pretty universal though, so the question
is mainly on different emote sources. For that, a separate MSC, [MSC1951](https://github.com/matrix-org/matrix-doc/pull/1951)
already exists, so it is reasonable to compare this one with that one:

### Comparison with MSC1951
MSC1951 defines a dedicated room as the only image pack source. This MSC, however, also allows you to bind image packs
to your own account, offering greater flexibility. In MSC1951 there can also only be a single image pack
in a room. This could be problematic in e.g. bridged rooms: You set some emotes or stickers from the matrix
side and a discord bridge would plop all the discord emotes and stickers in another pack in the same room.

The original sharing-focused idea of MSC1951 is still preserved: Once room types are a thing, you could
still easily have an image pack-only room.

MSC1951 defines a way to recommend using a pack of a different room - this MSC does not have an equivalent
to that. Instead, this MSC allows multiple image packs for a room, typically one where you already
chat in anyways. Furthermore, it allows you to enable an image pack to be globally available for yourself
across all rooms you are in.

The core difference is how MSC1951 and this MSC define the image packs themselves: In MSC1951 you have to
set one state event *per image*. While this might seem like a nice idea on the surface, it doesn't
scale well. There are people who easily use and want hundreds or even thousands of image packs accessible.
A simple dict of shortcode to mxc URI seems more appropriate for this.
Comment on lines +275 to +277
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one could equally well argue the opposite. Given that matrix events are limited to 65536 bytes, and each image is going to be 1000 bytes or so (counting the URL and the imageinfo), you're not going to fit many images in an event.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a go at calculating a rough estimate of how many images (as defined by this MSC) you could fit into a single 65535 byte state event: https://github.com/anoadragon453/emote-estimate

The result was ~400 emotes per pack. This actually seems fairly tolerable for every day usage, especially as Discord has a maximum of 250 emotes per server (assuming you pay for that limit bump).

The main concern I can see is bridging; Slack seemingly lets you have infinite custom emoji per workspace, so a bridge would need to split that up into multiple packs, which may look a little ugly in the UI.

But my opinion is that 400 emotes is fine for now, and people are getting along with the current limits. A future MSC could add a method for merging image packs defined in separate state events together, but I don't think the current limit is a blocker.


In general, MSC1951 feels like a heavier approach to image pack sources, while this MSC is more lightweight.

## Looking further
A couple of interesting points have been raised in discussions of this MSC that tangentially touch
custom emoticons. Each warrant an MSC for themselves however, as they touch more on how `<img>` is works.
- Figuring out how `<img>` should work with encrypted media.
- Allow SVGs in the `<img>` tag. Current problem: Clients typically try to thumbnail the mxc URL,
and most media repositories can't thumbnail SVGs. Possible solution: Somehow embed the mimetype.
- For stickers: Recommend rendering sizes / resolutions
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved