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.css library
|
pico.css provides classless version to build elegant interface |
Font face for headers | The Grand Hotel font
|
Suitable for vintage theme |
Drag drop files | The file drag drop api | Better UX |
Filter processing | The WebGL Image Filter library
|
A cool library that provides the core functionality |
Download image button | The Download.js library
|
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.