Soul Snap
Back in 2014 I had a little PHP web app for recording an animated GIF using Yahoo's popular Javascript library for animated GIFs, and posting them to App.net. The app was actually inspired by an app that @kosso had made, called DeskFace.com, which had gone offline. DeskFace was just for still images, but let you add filters to the images. Mine let you create animations but had no filters.
At the time, there was a real interest in seeing people sitting at their computers or phones, and I wanted to keep it up.
When I created pnut.io, I adapted it for pnut. But the Yahoo library for animated GIFs was failing me. It created very small frames, at very large sizes. 16 Megabytes for 30-second GIFs, and very few options to improve it. I tried using the "Say-Cheese" JQuery library, but it still made very large files.
In 2020, I rewrote the app, using basic Javascript for the front-end and Rust for the backend, with nginx sitting in front of Rust. Javascript captured frames from your webcam and let you manipulate them. Rust has a very capable library for animated GIFs, and file handling is very natural.
Rust has very little to do, once the Javascript assembles a list of images and uploads them.
Something like this:
let reader = ImageReader::new(Cursor::new(bytes))
.with_guessed_format()
.expect("Cursor io never fails");
match reader.decode() {
Ok(img) => {
// image processed
const PIXEL_SIZE: usize = 4;
let width = img.width();
let height = img.height();
let mut pixels = Vec::with_capacity((width as usize) * (height as usize) * PIXEL_SIZE);
for (_, _, px) in img.pixels() {
pixels.push(px.0);
}
let proper_image = Image {
pixels,
width,
height,
};
images.push(proper_image);
},
Err(err) => println!("err={}", err),
}
let images_slice = images.as_slice();
// encode animated gif at 3 frames per second
let gif = match engiffen(images_slice, 3, Quantizer::NeuQuant(3)) {
Err(why) => panic!("couldn't create GIF, {}", why),
Ok(gif) => gif,
};
match gif.write(&mut temp_file) {
Err(why) => panic!("couldn't write, {}", why),
Ok(response) => response,
};
I'd like to allow the user to configure the speed of the GIFs. And there are a lot of UI improvements to be made, especially being able to click-and-drag saved frames around the timeline, and being able to copy a frame.