Gramstash
Stuff
Created by potrace 1.10, written by Peter Selinger 2001-2011


.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

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)
Please note that all these examples are not feature complete. Our aim is to give a core understanding on the topic and a "starting point" for different frameworks, languages and implementation methods.

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 src 
        
The 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")
...

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] } }
Please check out our basic example here /uploaderExample

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;
}
Most PCs/Phones do not have the iconic Meme font loaded per default, so you probably want to get the "Impact" or "Anton" font from a CDN.

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.

Site:
Initial loading
Heap size  
Requests
page size
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)
If you have any comments please write us at info@meme-foundation.org.