Over the years I've had such a pain in the ass finding out exactly how discord embeds work. I love doing web development in my off time, and one of the most fascinating parts to me is creating apps that you can just post a link to in your friend group and have them all instantly be able to see whatever weird thing you're working on. A while back that led me to creating fxtwitter, which required some pretty jank setup to make work
It's strange. Over the years it feels like nobody has actually gone through and documented all possible interactions between embedded metadata and what appears on screen when you use it.. people vaguely are aware that OG tags work, and sometimes creative folks experiment with oembed endpoints, but I feel like no one has ever gone out of their way to actually go through and list what does work, what works weird, and what doesn't work at all
So I'm gonna try!
This is where I will document any limitations and abilities I find for embeds in a given chat protocol
These are each found through slow and tedious experimentation and research, along with personal experience from my work on fxtwitter
- site_name
- site_url
- theme-color
- Descripton ( up to 300 characters, rest is truncated )
- One image (can be enlarged by using
<meta name="twitter:card" content="summary_large_image" />)
- image arrays
- videos ( they just show up as a single static image )
- metadata tags for media ( ie music, movies, books, etc., )
- Videos
- Arbitrary iframes via the
htmltag. This follows a strict whitelist of sites that are able to use this.
As of writing these are only verified by skimming the source code. Probably will act very different after refactor
- If OpenGraph, Twitter Card, or oEmbed don't provide the Title, it can be extracted from the <title> tag
- Audio resolver supports mp3, .wav, .ogg, .flac, and .aac
- Video resolver supports mp4, webm, .ogv, .mov, and .avi
- All media appear to support metadata somehow
- Can also resolve media embeds with url parameters
- Bluesky
- API Provided ( packages/api/src/unfurler/resolvers/BlueskyResolver.tsx )
- Theme Color
- Author Icon, Name, and URL
- Description
- Image array with alt text
- Videos
- Footer ( Icon, Text )
- Subfooter ( Text ( X reposts, X likes ) )
- HackerNews
- API provided ( packages/api/src/unfurler/resolvers/HackerNewsResolver.tsx )
- Theme Color
- Author name
- Title name & link
- Description ( markdown supported )
- Footer ( Icon & Text )
- Tenor
- Scraped ( packages/api/src/unfurler/resolvers/TenorResolver.tsx )
- Simple GIFV direct media link
- it is very funny that they dont use the api for this
- Klipy
- Scraped ( packages/api/src/unfurler/resolvers/KlipyResolver.tsx )
- Doesn't appear to be working at the moment
- Probably works identically to Tenor
- Wikipedia
- API Provided ( packages/api/src/unfurler/resolvers/WikipediaResolver.tsx )
- Theme Color
- Title name & URL
- Description
- Thumbnail
- Xkcd
- Scraped ( packages/api/src/unfurler/resolvers/WikipediaResolver.tsx )
- Title name & url
- Image thumbnail with alt text
- Footer ( Alt text )
- Youtube
- API Provided ( packages/api/src/unfurler/resolvers/WikipediaResolver.tsx )
- Theme Color
- Provider name & url
- Author name & url
- Title name & url
- Embeded youtube iframe
- AcitivityPub
- API Provided ( packages/api/src/unfurler/resolvers/subresolvers/ActivityPubFetcher.tsx )
- Theme Color
- Provider Name and URL
- Author Name and URL
- Site Name and URL?
- Descripton
- Image array with alt text
- Videos
Genuinely, stellar support all around for opengraph. Many social and chat apps seem to have shoddy support for the 3 main embedding standards, leading to the kind of frustrated confusing I've had to work around with Discord specifically - Fluxer actually did the smart thing here and took the time to actually properly implement all of the relevant features of opengraph, which means you can make just about any embed possible simply by having your opengraph tags set up correctly. We love to see it. There is only one minor downside ( and honestly it's so minor that I might expect it to be a bug rather than an intentional design decision ) which is that the "Author Name" and "Author URL" parameters are only ever picked up from an oembed endpoint. So basically you need to spin up an entire oembed system just to fill out that one line ( I believe it's usually that medium white text underneath the provider name and above the Title )
- og:title
- og:site_name ( Seemingly this can be used in place of a provider_name value from an oembed endpoint )
- og:description
- og:theme-color
- og:image ( Properly implements image arrays up to 10 by default! )
- og:image:alt
- og:image:description (fallback if no alt text in :alt)
- og:image:secure_url
- og:video
- og:video:url
- og:video:secure_url
- og:audio
- og:audio:url
- og:audio:secure_url
- og:determiner
- og:url
- any extended namespaces ( ie movie and music metadata )
Twitter elements in Fluxer are seemingly not as useful as they are with Discord - The main use of twitter elements there is to get around the piss-poor opengraph support that has a ton of holes, and for manipulating the image size, because for some reason that's the only way to do that. Fluxer does have good support for twitter card tags, but most of them are largely reduntant in the context of building an embed for fluxer intentionally. Basically all of the functionality is covered by opengraph, with the interesting exception of twitter:player. I genuinely don't know how or why this is implemented, but I'm excited to test it, because this is one of the two ways that someone would potentially embed arbitrary HTML into an embed
- twitter:title ( fallback if no opengraph title is found )
- twitter:description ( ditto )
- twitter:site:name ( Seemingly this can be used in place of a provider_name value from an oembed endpoint )
- twitter:image ( og fallback )
- twitter:image:src ( og fallback )
- twitter:image:alt ( og fallback )
- twitter:video ( og fallback )
- twitter:video:url ( og fallback )
- twitter:player ( genuinely no fucking clue how this would work )
- twitter:player:stream ( ditto )
- twitter:audio ( og fallback )
- twitter:audio:url ( og fallback )
- twitter:card ( All element sizing is determined by the client )
- twitter:creator ( basically the @username field, seemingly just ommitted? ) ( can be provided via author_name in oembed )
From what I can tell, Fluxer in it's current state does not really read into oEmbed data on urls without a dedicated unfurler They seem to have instead opted to implement opengraph tags properly, supporting things like image arrays and video embeds Unfortunately, it also appears that there is a single field that can't be filled with opengraph tags that you need an oembed endpoint to fill out, being the auther_name and author_url. This is minorly frustrating because you have to spin up a whole endpoint to support one line of text, but the embed implementation is still a vast improvement over Discord's
It is a bit of a bummer to see that you can't just cram an iframe into an oembed response to make basically whatever you want, but I will wait for the refactor to see if this behavior changes. if not I may attempt to make a PR to implement it as an optional feature you can enable for self hosted instances full of people you trust. I do understand blocking it from a security standpoint but the potential for cool gizmos and gadgets is so enticing that I would actually suggest that the rendering of rich embeds be something the client can opt-in to play with
- author_url
- author_name
- provider_url
- provider_name
- basically everything else
Basically it's the stochastic ideal in comparison to discord, allowing you to do basically everything that you can possible do via simple meta tags in the header of your site - But there are still possible improvements - The absolute perfect implementation would be websites that can basically completely take advantage of the full embed object the same as a webhook or a bot could, but there are a few properties you can't set
- You can't provide a Author icon
- You can't provide Footers or Subfooters ( or an icon that would normally accompany that )
- Secondary images aren't possible as far as I can tell ( ie a site badge to display in the top right corner alongside a gallery of images )
The only other issue is that you are only able to provide an author_name and author_url via a dedicated oembed endpoint ( it would be way better if it was possible to use twitter:author ) but honestly this is such a minor gripe that I would probably assume this is a bug rather than a deliberate design decision
Fluxer's web embed implementation gets 4 big booms
These are found through light experimentation and user reports
- twitter:player (breaks the whole embed)
- twitter:description (breaks the whole embed)
I actually recently figured out why twitter:player breaks the entire telegram embed. It's because twitter:player's purpose is for embedding a livestream, and in order to do that, it needs to actually embed the player itself into the embed, which means it wants to embed HTML, which is obviously a big XSS vector, so is agressively disabled. ( speculation guy )