.meme docs
The .meme format is a standardized way to abstract a Meme Image or GIF into JSON.
It can be used as metadata standard for files or as a replacement for the Image/GIF itself.
{
"toptext":"Programmers than vs Programmers now:",
"text_content":[{
"text": "DECRYPTING ENIGMA",
"position":{"top": 34, "left": 10}
},{
"text": "PLEASE TURN OFF\nLIGHTMODE.\nMY EYES HURT",
"position":{"top": 60, "left": 65}
}],
"template":"https://www.gramstash.com/media/bigvscheems.jpg"
}
In the Docs:
What & Why
The Format .meme object
.meme -> Meme
Upload/Create a .meme object
Styling
Benchmarks
What & Why
The Format .meme object
.meme -> Meme
Upload/Create a .meme object
Styling
Benchmarks
# What & Why
We have two use cases which both make use of the .meme format:1. As an API that serves content in the .meme format to create Memes for a client.
2. As a metadata standard for static files to enable developers/search engines to work with/understand your content.
Check out the first option if you want to deliver content blazingly fast. Since we cache the template (image, video or GIF) and load the text objects via JSON, we can outperform the classic "load each file separately" approach
Reuse cached template
Choose the second option if you want to give third party developers an option to work with your content. This is also great for search engines since a clear structure in the metadata helps a crawler to understand your images and GIFs better.
But wait there is more. Saving and or adding metadata has more advantages like
make Memes searchable, Check for reposts? Easy!, translatable, create audio memes for blind people, decentralize via the IPFS Network and much more
# Format structure
Every .meme object needs two core properties template & text_content.Next to that we specified a number of optional attributes to give developers the option to enrich and properly represent the meme file. You can also add custom properties if you want to write a custom rendering engine, these are just the basics.
Core properties:
text_content [Array]
Is an Array of text objects (dict) with the position, styling rules and the text itself
text_object [Dict]
"text": "Hello world", "class": "meme-text" ,"position":{"top": X, "left": Y}
template [URL]
A URL for the Meme template.
Optional properties:
toptext [String]
A string that represents the text which is above a Meme
meme_name [Array]
A list that contains the name of the template/Meme (It's a list since a Meme can contain more than one template)
["Template Name 1", "Template Name 2"]
nsfw [Boolean]
Not safe for work is a Boolean that indicates that the Meme contains dark humor/nudity.
category [String]
A category for the post when you would like to sort by specific categories
render [Boolean]
A boolean to determine if template and text should be rendered or the fallback_url should be used.
This field should be set to "false" when a custom/photoshopped template is used,
but you still want to add Metadata for search engines.
title [String]
Self explaining
username [String]
Uploaded by ... to ensure the original author gets the credit
fallback_url [URL]
An Image url that has the meme as JPG,PNG etc. so if a problem occurs we can load the image as a fallback
reaction_post [Dict]
When a Meme was added to the bottom of another Meme. A reaction_post can have all the properties above including another "reaction_post".
Inside the "makeMeme" function we use a recursive approach to generate the Meme-Chain.
Examples
{
"toptext":"Programmers than vs Programmers now:",
"text_content":[
{
"text": "DECRYPTING ENIGMA",
"class":"meme-text-s",
"position":{"top": 34, "left": 10}
},{
"text": "PLEASE TURN OFF\nLIGHTMODE.\nMY EYES HURT",
"class":"meme-text-s",
"position":{"top": 60, "left": 65}
}
],
"meme_name":["Buff Doge vs. Cheems"],
"username":"Anita Burrito",
"template":"https://www.gramstash.com/media/bigvscheems.jpg"
}
If you want to check if your created .meme object works, paste your JSON or upload a file here Gramstash.com/format
# JSON -> Meme
For this step you either need an API endpoint that serves you memes in the .meme format or the requested images/GIFs have a .meme key (like this image) in its metadata.If you do not have an endpoint you can use our free IPFS endpoint (sometimes takes a minute due to the decentralized nature of IPFS Nodes)
const memeDict = { "Buff Doge vs. Cheems": "https://www.gramstash.com/media/bigvscheems.jpg", ... // Mapping the names to urls is better than using the template image // you do not depend on another media server + we can abstract the meme from a given resource } let makeMeme = (content) => { let post = `<div>${content["toptext"]}</div>` // container for css post += `<div class="meme-container">` // text elements content["text_content"].forEach(text => { post += `<span style="left:${text['position'].left}%;top:${text['position'].top}%" class="meme-text">${text['text']}</span>` }) // image post += `<img src="${content['template']}">` post += `</div>` // close container // append reaction Meme recursively if (Objects.keys("reaction_post").length) { post += makeMeme(content["reaction_post"]) } return post } let someMemeObj = callApiEndpoint(.... document.getElementById("container").innerHTML = makeMeme(someMemeObj)
Checking metadata from a resource (advanced)
When you know that a CDN serves files with a ".meme" header you can load the header only and build the Meme from that headers metadata rather than downloading the image itself:let checkImageHeader = (url, headerKey) => { return makeRequest({ // makeRequest is a placeholder for axois, ajax etc method: 'HEAD', //receive only the HEAD of a resource url: url }).then((xhttpResponse)=>{ let memeHeaders = parseHttpHeaders(xhttpResponse.getAllResponseHeaders(), headerKey) return memeHeaders.length ? JSON.parse(memeHeaders[0].replace(headerKey, "")) : false }).catch((reason) => { console.log(reason) }) } let parseHttpHeaders = (httpHeaders, headerKey) => { return httpHeaders.split("\n").filter(head => head.startsWith(headerKey)) } let url = "https://metamemes.s3.us-east-2.amazonaws.com/rollsafeMeta.jpg" let headerkey = "x-amz-meta-meme:" //x-amz = amazon s3 key (as an example for AWS) checkImageHeader(url, headerKey).then((value) => { if(value) return makeMeme(value) // create meme from .meme header via json else return `<img src=${url}....` // display the meme with img tag and srcThe Meme JPG has in its metadata the .meme representation of itself. As a developer you can now decide if you want to load the resource (jpg) or just the header and create the Meme from the metadata.
This approach is interesting if you want to opt in the whole .meme standard. You can still save your Memes as JPGs but add the .meme representation into the files metadata. Now anybody requesting the object can first check the header for a faster loading time due to text only download and reusing a cached template.
# Upload
let memeName = document.getElementById("memeNameInput") ...Please check out our basic example here /uploaderExample
let createMemeObject = () => { return { "template": memeImage.value, "text_content": [ {"text": firstText.value, "class": "meme-text-centered", "position": {"top":5}}, {"text": secondText.value, "class": "meme-text-centered", "position": {"top":85}} ], "category": "meme", "toptext": topText.value, "meme_name": [memeName.value] } }
Core Idea is to have input fields for Title, top text, Meme text, Meme Template Select etc. to create JSON and upload it to your server or upload this JSON alongside your JPG and save it as metadata if you prefer the static image way.
# Styling
Inside the text objects¹ we have a "class" attribute which refers to standardized styling classes. The main reason we did not enter css styling directly into the text object was that classes can be used generic."font-size:24px" works on the web but not on iOS,Android/Flutter while "meme-text-large" can be integrated platform independent.
.meme-text { font-family: Impact, Anton, Arial, sans, sans-serif; font-size: 1.7em; color: white; position: absolute; z-index: 1; -webkit-text-stroke-width: 1px; -webkit-text-stroke-color: #000; text-align: unset; } .meme-container { position: relative; overflow: hidden; } .meme-text-centered { width: 100%; text-align: center; left: 0; } .toptext { font-size: 1.3em; margin-bottom: 15px; font-family: Arial, Helvetica, sans-serif; }
There are more styling classes you can use, for a full overview please check our GitHub Repo.
# Benchmark
Classic image boards and social media sites loading times for content are so slow and memory intensive that it would not be fair/make sense to list them here. Also, there are rarely any "meme only" sites/apps.But to give a general idea:
You can render 1000 Memes (~500kb in size) in less than a second with the methods shown above. In that time most modern websites haven't even loaded their menu bar.
For a site with mixed content (non Meme and Meme post) like Twitter, Instagram, Reddit etc. it's comparable to our site Gramstash when you activate the "Gif" and "Things" switches.
Gramstash
>1s
>3mb
>30
~500kb
Your average website
~3-4s
~80Mb
~150
~15-20Mb
Please note that (like all benchmarks) these numbers have to be viewed with caution. Different websites/apps have different approaches on how to load content. Things like memory consumption, downloaded megabytes, CPU usage might not be as important for other sites as for us.
Keep in mind that Gramstash was build with high performance in mind at all times, while other sites might focused more on high quality images, showing various different content, more feature rich interfaces and so on.
# Notes
# Check for duplicates:Trigram search (Postgres)
Levenshtein distance (Wikipedia)
# Memes for memers with vision loss:
Example App (gramstash.com)
# Search:
Example App (gramstash.com) built with Meilisearch (GitHub)