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

MSC2448: Using BlurHash as a Placeholder for Matrix Media #2448

Open
wants to merge 52 commits into
base: old_master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
8ba6071
Add blurhash MSC
anoadragon453 Feb 27, 2020
f71535a
some cleanup
anoadragon453 Feb 27, 2020
8adf2b2
Add blurhash to img tag, make things optional, /upload mod
anoadragon453 Feb 28, 2020
56703fc
unstable prefixes, no encoding needed
anoadragon453 Feb 28, 2020
793107d
Move m.r.m msgtype into the m.r.m section
anoadragon453 Feb 28, 2020
777c30c
hm
anoadragon453 Feb 28, 2020
b80822e
data-mx-blurhash
anoadragon453 Feb 28, 2020
b240d92
Must HTML encode, link to blurhash algo description
anoadragon453 Feb 28, 2020
1afad01
New endpoint, not new key on existing endpoint
anoadragon453 Feb 28, 2020
60773e4
No HTML encoding warning
anoadragon453 Feb 28, 2020
df5b6d7
Straight mxc->blurhash endpoint
anoadragon453 Feb 28, 2020
571ce2a
Unstable feature flag
anoadragon453 Mar 3, 2020
e885eae
blurhash in "info", download->blurhash
anoadragon453 Mar 3, 2020
4b83c51
Merge branch 'anoa/blurhash' of github.com:matrix-org/matrix-doc into…
anoadragon453 Mar 3, 2020
b893c21
Get an image from a blurhash, get a blurhash from an MXC url
anoadragon453 Mar 3, 2020
6e8eb59
Merge branch 'anoa/blurhash' of github.com:matrix-org/matrix-doc into…
anoadragon453 Mar 3, 2020
3695ecf
No GET body
anoadragon453 Mar 3, 2020
842c2a0
Update proposals/2448-blurhash-for-media.md
anoadragon453 Apr 3, 2020
3994010
Explicitly state blurhashes are not exempt from encryption
anoadragon453 Aug 18, 2020
616bc81
/upload -> xyz.amorgan.blurhash instead
anoadragon453 Aug 18, 2020
e0a7442
Add blurhashes to URL previews
anoadragon453 Jan 26, 2021
385be8a
Clarify that 'blurhash' should go in content.info
anoadragon453 Jan 26, 2021
708b756
formatting
anoadragon453 Jan 26, 2021
b761b06
Add blurhash to m.sticker events
anoadragon453 Jan 26, 2021
40d71ff
Add m.room.member
anoadragon453 Jan 26, 2021
7f13184
Add m.room.avatar
anoadragon453 Jan 26, 2021
1300a6e
Update unstable prefixes section for new event types
anoadragon453 Jan 26, 2021
2a02d2c
Specify endpoints that will need to be modified to support avatar blu…
anoadragon453 Jan 26, 2021
7ea82b4
Remove the controversial /_matrix/media/r0/blurhash endpoint
anoadragon453 Jan 26, 2021
f93d708
Format the Unstable prefixes section a little more nicely
anoadragon453 Jan 26, 2021
ed9ed5e
grammar
anoadragon453 Jan 26, 2021
48c4d55
Add a quick note about encrypted media and server-side blurhash calc
anoadragon453 Jan 26, 2021
fba60db
Rename with a more generic title
anoadragon453 Jan 26, 2021
c837c83
Riot -> Element
anoadragon453 Jan 26, 2021
b3f1915
Slight title adjustment
anoadragon453 Jan 26, 2021
5b8c191
Add note about negligible DoS potential of large blurhashes
anoadragon453 Jan 26, 2021
63d4966
Add note about invalid BlurHashes
anoadragon453 Jan 26, 2021
676571f
Move blurhash to m.room.avatar's content.info dict
anoadragon453 Apr 1, 2021
a302197
Merge branch 'master' of github.com:matrix-org/matrix-doc into anoa/b…
anoadragon453 Apr 1, 2021
594bcee
Apply suggestions from code review
anoadragon453 Apr 13, 2021
64116ae
Clarify that clients must calculate blurhashes for encrypted media
anoadragon453 Apr 13, 2021
9bd0ba6
Fix inconsistency of blurhashes MUST vs SHOULD be omitted
anoadragon453 May 22, 2021
e7e0fb7
blurhashes can be inserted into more than just m.room.message events
anoadragon453 May 22, 2021
1d954f0
Remove the need for an unstable_features entry
anoadragon453 May 22, 2021
9e981ba
Make blurhash generation on the server opt-in
anoadragon453 May 22, 2021
75a4fa6
Clarify why we place blurhash under the matrix:image:blurhash OG prop
anoadragon453 May 22, 2021
934b6d7
Mention that the generate_blurhash query param needs an unstable prefix
anoadragon453 May 22, 2021
974d368
lint proposal markdown
anoadragon453 Aug 1, 2022
5668397
Apply suggestions from TravisR
anoadragon453 Aug 1, 2022
234877c
Clear client recommendations for blurhashes vs. thumbnails
anoadragon453 Aug 1, 2022
abf5283
r0 -> v3; update links to new spec website
anoadragon453 Aug 1, 2022
754fa7a
Add and update request/repsonse examples
anoadragon453 Aug 1, 2022
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
377 changes: 377 additions & 0 deletions proposals/2448-blurhash-for-media.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,377 @@
# MSC2448: Using BlurHash as a Placeholder for Matrix Media

[BlurHash](https://blurha.sh) is a compact representation of a placeholder
for an image (or a frame of video). Currently, in Matrix, clients must
display a placeholder image in the message timeline while a piece of media is
loading. Some clients, such as Element, simply display an empty space.

While thumbnails exist to combat this to some degree, they still need to be
downloaded from a homeserver, which is not instantaneous.

Instead, a BlurHash can be sent inside events, which upon
receipt other clients can render for a pretty media preview while the actual
thumbnail downloads. They also do not contain any `"` characters, making them
simple to stick inside existing JSON blobs.

To be clear: A BlurHash does not replace a thumbnail - rather it is a placeholder
and should be shown before the thumbnail is downloaded. For a familiar messaging
user experience, clients are recommended to first render and display a blurhash,
then download the thumbnail of the media. Once the thumbnail file is downloaded,
display it. Finally, the user can optionally view the full media item by selecting it.

## Proposal
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

### m.room.message

An optional field is added in `m.room.message`'s `content.info` dictionary
with the key `blurhash`. It is a BlurHash of the original piece of media.
Clients could then render this using [one of the available BlurHash
implementations](https://github.com/woltapp/blurhash).

This would be optionally displayed while the thumbnail of the media is loaded
in parallel.

Example `m.room.message` content:

```json
{
"body": "image.png",
"info": {
"size": 149234,
"mimetype": "image/png",
"thumbnail_info": {
"w": 301,
"h": 193,
"mimetype": "image/png",
"size": 72958
},
"w": 301,
"h": 193,
"thumbnail_url": "mxc://example.org/abcdefg",
"blurhash": "JadR*.7kCMdnj"
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
},
"msgtype": "m.image",
"url": "mxc://example.org/abcde"
}
```

Note that a BlurHash representation is really only applicable to media, and
as such should only be used in conjunction with the following
`m.room.message` msgtypes:

* `m.image`
* `m.video`

### m.sticker
Copy link
Member

Choose a reason for hiding this comment

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

Slight hurdle, blurhash doesn't support transparencies so doesn't seem appropriate for Stickers

image
image

The reference site breaks when given a transparency
image

woltapp/blurhash#100

Copy link
Member

Choose a reason for hiding this comment

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

random thought: it might be worth defining a "shape" next to the blurhash where a polygon of max 12 points (or whatever) can be defined

Copy link
Member

Choose a reason for hiding this comment

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

Sounds like you want to maintain cross-platform libraries which extend blurhash with generic path masking :D

Copy link
Member

Choose a reason for hiding this comment

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

I now realize this is a no-go. A square sticker isn't that bad: clients might want to put a circle or something around it as a border to match the rough shape of most stickers.

Copy link
Contributor

Choose a reason for hiding this comment

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

One hacky solution is to generate a blurhash on black and a blurhash on white, the client can then pick the once closest to the theme. But that doubles the storage space. If you wanted to get super fancy a client can probably diff the two and tease the transparency info out. But I don't want to mandate that in the spec. (I would rather wait for a blurhashv2 to come out with transparency support).

Copy link
Member

Choose a reason for hiding this comment

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

tbh I disagree, only because stickers look broken with a full-frame blurhash.

Copy link
Member Author

Choose a reason for hiding this comment

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

The current state of blurhash not supporting transparency (and little movement on this front from the upstream library) is a good sign to potentially back away from supporting blurhashes for m.sticker events at the moment, and leave it as a separate optimisation as @sumnerevans mentioned.

The path data sounds like a potential solution, and other messages services such as Telegram indeed use that method to great effect. Some thought will need to be given to animated stickers though, and what frame should be used when capturing a silhouette of the media (the first frame would not always be effective).

Blurhashes in a sticker picker also seems less useful than silhouettes when searching for a known sticker on a slow network connection.

Choose a reason for hiding this comment

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

I'm curious whether ThumbHash would address this sufficiently: https://github.com/evanw/thumbhash

Copy link
Member

Choose a reason for hiding this comment

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

@cvwright thanks for bringing that up, not heard of it before and the comparison looks good visually - https://evanw.github.io/thumbhash/

Only has few implementations currently though

image

Copy link
Member Author

Choose a reason for hiding this comment

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

I still think the "rough path outline" solution might be more visually appealing for stickers? Observe how Telegram employs it while stickers are loading in the picker. I think this would be much more appealing than blurry thumbnails:

image

We shouldn't discount using ThumbHash elsewhere though (see the other discussion on it #2448 (comment)).


An optional field is added to `m.sticker`'s `content.info` dictionary with
the key `blurhash`. Its value is a BlurHash of the sticker media.

Example `m.sticker` content:

```json
{
"body": "Landing",
"info": {
"h": 200,
"mimetype": "image/png",
"size": 73602,
"thumbnail_info": {
"h": 200,
"mimetype": "image/png",
"size": 73602,
"w": 140
},
"thumbnail_url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP",
"w": 140,
"blurhash": "JadR*.7kCMdnj"
},
"url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP"
}
```

### m.room.avatar

Room avatars having BlurHashes available will be especially useful when
viewing a server's Public Rooms directory.

An optional field is added to `m.room.avatar`'s `content.info` dictionary with the
key `blurhash`. Its value is a BlurHash of the media that is pointed to by
`url`.

Example `m.room.avatar` content:

```json
{
"url": "mxc://matrix.example.com/a59ee02f180677d83d1b57d366127f8e1afdd4ed",
"info": {
"blurhash": "JadR*.7kCMdnj"
KitsuneRal marked this conversation as resolved.
Show resolved Hide resolved
}
}
```

### m.room.member

Much like room avatars, user avatars can have BlurHashes as well. There is a
little more required to implement this, but the outcome of no longer having
missing avatars upon opening a room is worthwhile.

An optional field is added to `m.room.member`'s `content` dictionary with
the key `blurhash`. Its value is a BlurHash of the media that is pointed
to by `avatar_url`.

Note that `blurhash` MUST be omitted if `avatar_url` is not present.

Example `m.room.member` event content:

```json
{
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Alice Margatroid",
"membership": "join",
"blurhash": "JadR*.7kCMdnj"
Copy link
Member

Choose a reason for hiding this comment

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

hmm: why is this on the top level but the room avatar isn't? should we just move the room avatar one up a level and call it good?

Copy link
Member Author

Choose a reason for hiding this comment

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

The justification for it is that m.room.avatar currently contains an info field (which the spec defines as type ImageInfo, which is similar to the info field of the m.image msgtype for an m.room.message event.

The inconsistency is indeed a bit awkward. If we move blurhash to the top-level of the m.room.avatar event type, then we're inconsistent with the m.image msgtype. We could go the other way and put an info (image_info?) field in the m.room.member event type, but that feels out of place for an event type whose primary purpose isn't media-related.

I'm curious what others think the best way forward is.

}
```

### Profile endpoints

Endpoints that return profile information, and thus MXC URLs to user avatars, are
extended to optionally include BlurHashes as well.

[`GET /_matrix/client/v3/profile/{userId}`](https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3profileuserid)
has an optional field added with
the key `blurhash`. Its value is a BlurHash of the media that is pointed to
by `avatar_url`. `blurhash` MUST be omitted if `avatar_url` is not present.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
The same applies
to [`GET /_matrix/client/v3/profile/{userId}/avatar_url`](https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3profileuseridavatar_url)
, and to the federation
endpoint [`GET /_matrix/federation/v1/query/profile`](https://spec.matrix.org/v1.3/server-server-api/#get_matrixfederationv1queryprofile)
.

#### Example responses:

`GET /_matrix/client/v3/profile/{userId}`

```json5
{
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
"displayname": "Alice Margatroid",
"blurhash": "oyp8ky2BWn7VHEL"
}
```

`GET /_matrix/federation/v1/query/profile`

```json5
{
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
"displayname": "Alice Margatroid",
"blurhash": "oyp8ky2BWn7VHEL"
}
```

`GET /_matrix/client/v3/profile/{userId}/avatar_url`

```json5
{
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
"blurhash": "oyp8ky2BWn7VHEL"
}
```

Separately, [`PUT /_matrix/client/v3/profile/{userId}/avatar_url`](https://spec.matrix.org/v1.3/client-server-api/#put_matrixclientv3profileuseridavatar_url)
has an optional field added
to the request body with the key `blurhash`. Its value is a BlurHash of the media that is pointed to by the value of
the `avatar_url` field in the same request.

#### Example request/response interaction

Request:
```
PUT /_matrix/client/v3/profile/{userId}/avatar_url HTTP/1.1

{
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
"blurhash": "oyp8ky2BWn7VHEL"
}
```

Successful response:

```
200 OK

{}
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
```

### URL previews

An optional attribute is added to the OpenGraph data returned by a call
to
[`GET /_matrix/media/v3/preview_url`](https://spec.matrix.org/v1.3/client-server-api/#get_matrixmediav3preview_url)
called `matrix:image:blurhash`. The value
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
of this attribute is the blurhash representation of the media specified
by `og:image`. Note that we place this value under the `matrix` namespace as
the [OpenGraph protocol](https://ogp.me/) does not (yet) have a spec'd field
for BlurHashes.

Note that `matrix:image:blurhash` MUST be omitted if `og:image` is not present
in the response.

Example response to `GET /_matrix/media/v3/preview_url`:

```json
{
"og:title": "Matrix Blog Post",
"og:description": "This is a really cool blog post from matrix.org",
"og:image": "mxc://example.com/ascERGshawAWawugaAcauga",
"og:image:type": "image/png",
"og:image:height": 48,
"og:image:width": 48,
"matrix:image:size": 102400,
"matrix:image:blurhash": "oyp8ky2BWn7VHEL"
}
```

### Inline images

An optional attribute is added to `<img>` tags in messages:
`data-mx-blurhash`, where the value of the attribute is the blurhash
representation of the inline image.

This would be optionally displayed while the inline image itself is loaded in
parallel.

Example `m.room.message.formatted_body`:

```
"formatted_body": This is awesome <img alt=\"flutterjoy\" title=\"flutterjoy\" height=\"32\" src=\"mxc://matrix.example.org/abc\" data-mx-blurhash=\"LEHV6nWB2yk8pyo\" />
```

## Calculating a blurhash on the server

BlurHashes are inserted into events by the client, however some clients may not
be able to implement the BlurHash library for whatever reason. In this case, it
would be nice to allow the media repository to calculate the BlurHash of a piece
of media for the client, similar to how thumbnails are calculated by media
repositories today.
Comment on lines +253 to +257
Copy link
Member Author

Choose a reason for hiding this comment

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

It's roughly two years on. Are there any valid use cases that we're aware of for this endpoint today? I don't know of any clients that have needed to rely on it.

Copy link
Contributor

Choose a reason for hiding this comment

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

No. Element sometimes relies on the media repo for thumbnails, but that doesn't work in encrypted rooms anyway and I haven't seen any client using that endpoint nor any server supporting it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right. I'm going to rip it out of this MSC then. cc @turt2live who originally(?) proposed it.

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 been chasing memory allocation issues related to blurhashes in matrix-media-repo (MMR), and my conclusion is I agree with this conclusion.

Element is not using the returned field, and most/all of the bridges I can test quickly aren't using it either. This makes it fairly pointless for servers to support.


For added context, the allocation issue is with the input for calculating a blurhash. The blurhash itself is a non-issue.

image

Specifically, jpegs are awful.


A new boolean query parameter, `generate_blurhash`, is added to
[`POST /_matrix/media/v3/upload`](https://spec.matrix.org/v1.3/client-server-api/#post_matrixmediav3upload)
which allows clients to ask the server to generate a BlurHash for them. This
may be useful for clients that are designed to run on very low-power hardware,
or otherwise cannot generate a BlurHash itself.
If set to `true`, the server SHOULD generate a BlurHash of the uploaded media
and return it as the value of the `blurhash` key in the response.

If the server cannot generate a BlurHash of the media - perhaps because it is
not a file that a BlurHash can be derived from or it is too expensive to process
a very large piece of media - then the server SHOULD NOT return a `blurhash` key
in the response. Additionally, if `generate_blurhash` is not `true`, then the
server SHOULD NOT return a `blurhash` key in the response.

Fundamentally, this means that clients SHOULD NOT assume that a server will always
return a BlurHash in the response to `/_matrix/media/v3/upload`, even if they have
set the `generate_blurhash` query parameter to `true` in the request.

An example request from a client that would like the server to generate a
blurhash would look like:

```
POST /_matrix/media/v3/upload?generate_blurhash=true&filename=My+Family+Photo.jpeg HTTP/1.1
Content-Type: image/jpeg

<bytes>
```

Example response:

```
{
"content_uri": "mxc://example.com/abcde123",
"blurhash": "LKO2?U%2Tw=w]~RB"
}
```

We explicitly make this behaviour opt-in as it is assumed that the majority of
clients that end up supporting BlurHashes will be capable of generating them
locally. Thus, the less load we can put on the homeserver (by not making
blurhash generation the default) the better.

Note that media servers will not be able to return a BlurHash string for
encrypted media; that must be left to the client.

The server could additionally return the BlurHash string for an image when
given an MXC URL. This would be through something like the Media Information
API (specified in
[MSC2380](https://github.com/matrix-org/matrix-doc/pull/2380)), or similar.

## Visualisation

Viewing an image message that is loading:

![blurhashed preview](images/2448-blurhash.png)

Once the image loads:

![the image has loaded](images/2448-loaded-image.png)

As a sample, this is Element's behaviour prior to this MSC's introduction:

![boo, sad](images/2448-current-state.png)

## Alternatives

We could include a base64 thumbnail of the image in the event, but blurhash
produces much more efficient textual representations.
Comment on lines +323 to +326
Copy link
Member Author

Choose a reason for hiding this comment

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

Discuss the alternative algorithm (and self-proclaimed improvement over BlurHash) ThumbHash.

Copy link
Contributor

Choose a reason for hiding this comment

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

The alpha support is nice.

Copy link
Member Author

Choose a reason for hiding this comment

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

A comparison of ThumbHash vs. BlurHash.

Pros

Alpha support

ThumbHash handles images with transparent backgrounds in a much nicer fashion than BlurHash. While the obvious use case for this is stickers, as mentioned elsewhere, I think that vector outlines would be the best way to represent stickers before they are downloaded in full resolution.

Still, being able to somewhat make out what an image with a transparent background is before it has loaded is valuable.

image

Better quality

ThumbHash appears to generally create a better representation of the image than BlurHash (examples taken from https://evanw.github.io/thumbhash/):

image

image

image

Cons

Limited Library Support

BlurHash was one of the first to widely publicise this use case, and thus it is a lot more popular than ThumbHash. Compare the number of implementations for ThumbHash versus BlurHash.

Still, the algorithm is so simple that you could presumably translate it into your chosen language in about 30m.

BlurHash is already widely used in Matrix

Again, due to BlurHash coming out much earlier than ThumbHash, Matrix clients have already implemented BlurHash (through this MSC) widely. If we switch, clients with concern for backwards-compatibility will likely need to implement both BlurHash and ThumbHash.

However, currently BlurHash mostly applies to media sent in the timeline, which quickly becomes stale. Element Web only supports m.image events in the timeline. Nheko's BlurHash support extends to both stickers and map previews on location events. I'd be most concerned about room/user avatars, which rarely change. But I've not yet seen clients implement support for that yet.

The more pressing concern would be interacting with older clients that still only send BlurHashes instead of ThumbHashes. However the failure mode here during the transition period wouldn't be too bad - you just won't see blurred thumbnails.

Bandwidth

TODO: I'd like to conduct a test of both algorithms over a range of, say, 100 images. Using base83 encoding for both. Currently I'm not sure whether BlurHash or ThumbHash generally produces smaller encoding sizes.

Copy link

Choose a reason for hiding this comment

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

ThumbHash is also around 10x faster to generate, at least on iOS. I can run a little experiment and report the results if there's interest.

The Circles client is no longer generating BlurHashes. We will continue to display them but we are moving entirely to ThumbHash for the future. The slow performance was a big part of this, but also ThumbHash just seems to work better all around. For example, we were having issues on Android where the BlurHash code crashed the app on an invalid input.

Copy link
Member

Choose a reason for hiding this comment

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

As the person who implemented Blurhash based on this MSC in Element Web I'm all for switching to something which supports alpha and has better performance, maintaining blurhash rendering similar to how @cvwright described another client handling it for some time is acceptable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thumbhash shouldn't be significantly faster. What you are probably seeing is that most of the thumbhash libraries downscale images to 32x32 pixels before generating the hash, while blurhash libs expect the library user to do that. If you do such an experiment, make sure you take that into account.

Copy link

Choose a reason for hiding this comment

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

No, I downsample the source image once to 100x100. Then from that 100x100 I create the ThumbHash and the BlurHash.

Maybe the Swift ThumbHash implementation is just more optimized than the BlurHash version? The author went to some pretty great lengths to make it fast.

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look particularly optimized, but neither was the blurhash implementation, I guess. But feel free to bench it. Seems like I was also wrong, the hashing doesn't downscale first, but rendering the blurhash does create a 32x32 pixels image at best, which would be 10x faster than creating a 100x100 pixels image. So would be interesting to see comparisons, especially if you include my lib: https://github.com/Nheko-Reborn/blurhash :)

Copy link
Member Author

Choose a reason for hiding this comment

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

@cvwright If you are doing a benchmark, reporting on the resulting filesizes of the hashes would be appreciated!

Copy link
Member

Choose a reason for hiding this comment

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

@anoadragon453 if this is to progress we probably need to update the MSC to define the field for the thumbhash to live. The lack of alpha channel in blurhash makes it insufficient in my opinion.

I have a PoC impl for EW which sends & renders both blurhash & thumbhash, preferring the latter for rendering when available.

Blurhash
image
Thumbhash
image
Original
image

PoC: content["xyz.amorgan.thumbhash"] base64 encoded matrix-org/matrix-react-sdk#12164


## Backwards compatibility

Older clients would ignore the new `blurhash` parameter.

Newer clients would only show it if it exists.

Users who have not specified `blurhash` in their `m.room.member` event yet may
stand out from users who have while both are loading. This is entirely up to
clients to handle, though a suggestion may be to "fake" a blurhash by
blurring some placeholder image (perhaps something with variation between
users like Element's coloured backgrounds with letters in them, or [an
identicon](https://en.wikipedia.org/wiki/Identicon) derived from the user's
ID) until the user's actual avatar loads.

## Unstable prefixes
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

Implementations wishing to add this before this MSC is merged can do so with
the following:

* The `blurhash` key in any events, request or response bodies should be
replaced with `xyz.amorgan.blurhash`.

* `/_matrix/media/v3/upload`'s new `generate_blurhash` query parameter
should instead be `xyz.amorgan.generate_blurhash`. And instead of the
key `blurhash`, the endpoint should return `xyz.amorgan.blurhash`.

* The `data-mx-blurhash` attribute in `<img>` tags should be replaced with
`data-xyz-amorgan-blurhash`.

## Security considerations

BlurHash entries in encrypted events, be it as part of the `info` property,
or `<img>` tags, should be encrypted along with the rest of the event
content.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

A [discussion in
#matrix-spec](https://matrix.to/#/!NasysSDfxKxZBzJJoE:matrix.org/$Cfa0dtF3DenIUAbC5aeg3Xo10gAF54mAJLZ6VzvYNfo?via=matrix.org&via=amorgan.xyz&via=pixie.town)
questioned whether massive BlurHashes may be a potential DoS vector for
clients. The discussion found that only a maximum of 100 x 100 components can
be defined by a BlurHash. This may be in the higher range for low-resource
(or unoptimised) clients, and clients are free to refuse to render a BlurHash
with a large component count, but it shouldn't be a cause for concern.

Invalid BlurHashes should not be rendered.

## Links

BlurHash's algorithm description can be found
[here](https://github.com/woltapp/blurhash/blob/master/Algorithm.md), which
also includes the full output character set.
Binary file added proposals/images/2448-blurhash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added proposals/images/2448-current-state.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added proposals/images/2448-loaded-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.