PFME was built with Nuxt.js on the frontend and Node.js on the backend. The site allows a user to experience a one-stop framing shop, customizing just about every aspect of their finished product. Photo cropping, print size, glass type, frame, mats, paper quality — if it can be customized in a physical framing store, odds are pretty good it can be customized on the site.
It makes use of Shopify's API to handle the actual transaction, leading to an off-site checkout. Of note, it went through quite a few iterations, hammering home the importance of managing scope creep.
By far the most interesting aspect of this project was the ability to preview a customer's framing project in real-time as they made their decisions. The entire preview is printed to a canvas element, carefully calculated to display at roughly the actual size that it should turn out in the final product.
I say roughly, since the inner mats are always less than a quarter of an inch thick. At some resolutions, that gave them around a pixel on the canvas to be represented. With only a pixel to display it, "close enough" had to do.
For all of the frames, I had them prepared as a single file containing a square representation of the frame. From there, I could get away with chopping that image into eight parts for most of the frames. Four corners, and four lengths that could be stretched between the corners.
However, that method ran into a bit of a problem. Most frames were fine after being stretched, since the details that made up the lengths were gradients with hardly any details. Some frames weren't pleased with this approach, looking awful and warped when it was done. To solve this, I had to construct the frames slightly differently.
A single section of the length was cropped in such a way that the length could tile. From there, the corners were created by overlying the tiling lengths at right angles and cutting them. With the frame assets created like this, I was no longer constrained to just stretching the lengths between corners; now I could both stretch and tile the lengths between the corners.
I'm sure if you look hard enough, you'll be able to find where the frame tiles below. However, I'm quite happy with how it turned out. Most users will just see the frame as intended.