Ever wonder if you can build such an offline alternative to the gram from the meta to use the vintage filters you love while avoid all the metal health issues? Wonder no more, 'cause today, in this article, I'll show you how to build an offline one that should be called Vintagram ๐ Have fun using it then ๐
The Glitch, the Zuck and the Offline
I uploaded this one to glitch, so you can both use it online at https://vintagram.glitch.me/ and offline just by downloading files or copying the source codes I pasted here while avoid the Zuck completely, is it cool? ๐
Download the files for offline use
In order to use vintagram offline, you need to download the files at
- https://vintagram.glitch.me/index.html
- https://vintagram.glitch.me/webgl-image-filter.js (or from github)
- https://vintagram.glitch.me/download.js (or from github)
- https://vintagram.glitch.me/run.js
and run a local webserver to serve them.
Some little intro
I write code mainly just for fun and I think it's necessary to have fun in life, 'cause it's too short ๐. My main interest now is to find some cool libraries, especially javascript and thinking how to have fun using them ๐. There are actually tons of libraries to have fun with and I hope I have time in the future to do so :D Vintagram is an idea I have when I found many cool effect libraries and there are not many people willing to turn them into something fun to use. So I decide to build one ๐, and I'll show you how to do it too ๐
Technique details
I list the technique details here so you can easily follow the article
| Technique | Solution / Library used | Note | 
|---|---|---|
| Building a simple and clean interface | The pico.csslibrary | pico.cssprovides classless version to build elegant interface | 
| Font face for headers | The Grand Hotelfont | Suitable for vintage theme | 
| Drag drop files | The file drag drop api | Better UX | 
| Filter processing | The WebGL Image Filterlibrary | A cool library that provides the core functionality | 
| Download image button | The Download.jslibrary | For quick implementation of the download button | 
| Copy Paste skill | For better coding ๐ | 
Understand how it works
Interface structure
The interface structure is quite clear. You can view the source code to see the html code and some css then. The pico.css and font can be served by the cdn. Just copying the code they give you and that is done.
App structure and flow
Vintagram is designed that at first, you load a photo to the page via drag drop (desktop / laptop usage) or the upload photo button (smartphones, ...). The photo then get displayed and clicking on the filter image will apply that effect. The download button is for saving image or use can use save image feature from the browser.
Dragdroping file
This is straightfoward as you'll just have to implement the listeners for the dragover and drop events. For quick building, I listen on the whole viewport, that is, the html element. The handleDrop function handles the droping while handleDragOver funtion is just for preventing the default action from the browser. Look at the code
function handleDrop(ev) {
    ev.preventDefault();
    if (ev.dataTransfer.files && ev.dataTransfer.files.length == 1) {
        let [f] = ev.dataTransfer.files;
        loadFile(f);
    }
}
So to extract the dropped file, you query the ev.dataTransfer.files property. The length is just for ensuring that user only drop 1 image at a time. Also remember to prevent the default action as well. After that the loadFile function will handle the file. 
You can infer the code for the upload photo button
function handleFileChange(ev) {
    ev.preventDefault();
    if (ev.target.files && ev.target.files.length == 1) {
        let [f] = ev.target.files;
        loadFile(f)
    }
}
to handle this situation. This time, to get the file, you query the ev.target.files property rather than ev.dataTransfer.files above and the event you need to listen to here is  change event from the hidden file input of the page. Again, the loadFile function is called to do the job.
Loading the image
In order to load and display the image, I maintain 2 image variable: the display variable for displaying result and the inner variable for handling loading behind the scene. Also, inner is for checking if the given file is a valid image (also for keeping the original version of the image). That's why I listen to the onload and onerror event from inner. There are also two url variable, current and received to maintain the current url and the one being received.
If a valid image is loaded, discard the current url and replace it with the received one. If an invalid image is loaded then discard the received one. In the case of a valid image, we also display it by passing the url to the display image and from the usage of the webgl-image-filter, we have to create a new filter variable (this requires a canvas with the width and height of the image) and hence the code
inner.onload = function () {
    let canvas = document.createElement('canvas');
    canvas.width = inner.naturalWidth;
    canvas.height = inner.naturalHeight;
    filter = new WebGLImageFilter({ canvas });
    display.src = this.src;
    if (current)
        URL.revokeObjectURL(current);
    current = received;
    received = null;
}
We are good to go with loading the image then.
  
  
  Using WebGLImageFilter
The main point of the webgl-image-filter library is to create a filter, which we did in the onload listener, then call addFilter function for each filter you want to add. There is also the reset function in case you want to return to the beginning (will clear all the added filters) and finally, using apply to apply the filter to the image. The result will be drawn to the canvas given to the filter in the listener above. After that, you can use the data from the canvas. Here is the example of the filter variable usage
filter.reset()
filter.addFilter('hue', 15);
filter.addFilter('saturation', 0.2);
filter.addFilter('brightness', 0.2);
filter.addFilter('contrast', 0.2);
filter.apply(theImage).toDataURL(); // get the data url from the drawn canvas
I find this library kinda cool because the author provided some useful filters that you can use immediately. So rather than knowing how to use hue, saturation, brightness, contrast,... properly, you can use instantly use the polaroid effect with
filter.reset()
filter.addFilter('polaroid'); // cool!!!
filter.apply(theImage).toDataURL(); // get the data url from the drawn canvas
This article is mean to show you how to build an simple app like vintagram so I won't dig deeper into how to build filters, but rather focus on using the library and the presets. You will have to find more about filters in another articles or try building one.
Applying the filter
The idea here is simple: using a preset and transfer the data url to the display image. I build some filter with associated  name in the presets variable so you can use them easily by calling the applyPreset function which will find and apply the filter as following code shown
function applyPreset(name) {
    if (!filter || !(name in presets))
        return;
    filter.reset();
    presets[name](filter);
    display.src = filter.apply(inner).toDataURL();
}
Download the image
This is quite simple: use the download function provided by the download.js library through the downloadImage wrapper
function downloadImage() {
    if (!filter)
        return;
    download(display.src, "vintagram.png");
}
Well, and that's vintagram. Hope you enjoy the app, the article and learn something new. Have a nice day then ๐
 

 
    
Top comments (2)
And here's me thinking this would be an Instagram clone for wine lovers ๐ท
Nice concept but downloaded image format is not working. Take a look on this issue.