Hey folks, (I hope this counts as on topic) I got into cross-stitching in the last year and recently thought I’d cross-stitch the Elm logo. Colour matching is tricky and I regret cutting the border so close but I’m happy with the result.
I’ve hosted two pattern PDFs here:
For the Full Colour and White on Blue variations. (I’m especially skeptical of choice for the blue in the White on Blue pattern. The PDF looks OK but that actual thread color in real life is a bit different. I need to work on my Thread <-> Rgb table)
I really recommend cross-stitching to anyone looking to make things with their hands. It has a pleasant relationship with pixel art too if you have some favourite old school computer game graphics that you’d enjoy reproducing. YouTube has lots of tutorials if you’re curious how to go about it. This is the first one I watched. And Reddit has an active community at r/crossstitch.
Some Elm is involved too! I originally made this piece using some Rust code to analyse a scaled down bitmap of the Elm logo but more recently I’ve built a little editor for personal use that uses Elm for the UI and the final pattern layout and then uses Typescript, Canvas & Rust->Webassembly for the pattern editing. Currently it is closed source unfortunately as I try to assess any potential value it in but it has been fun to experiment with this set of technologies. The Rust/Webassembly part was build working from the rust wasm tutorial (https://rustwasm.github.io/docs/book/game-of-life/implementing.html) but starting with the rust-webpack template (https://github.com/rustwasm/rust-webpack-template.)
Complication: Single Source of Truth
One of the main problems I’ve faced is managing a single source of truth for each piece of data in the editor when the image state is in Rust and some UI state is in Elm and other parts in Typescript. I have previously used the elm-canvas library for some experiments but feared performance would take a hit, especially getting data from Rust/Webassembly to Elm, so I’ve kept some code in Typescript to interact with the canvas directly. It seems possible to do canvas interactions from Rust too using the web-sys modules but that would still leave some state in Rust and some in Elm.
Not the end of the world but it means you have to think quite hard about where the source of truth is and where other cached copies of the value might be. I found myself wanting to feed data directly from the typescript layer into the view function of the Elm code so that I didn’t have to have the duplication in the model. Clearly problematic for other reasons but interesting to find myself wishing for that.
Complication: Decoding Structured Data Out of Int Arrays
Another issue I had was transferring the image data from Rust to Elm for the final PDF layout. The editing is done on a canvas but I already had code to do the layout in Elm in Svg so I’ve taken that route. You can only really communicate via Rust & Typescript using number values including various kinds of int arrays. So when I extract some line data from rust (there aren’t any lines in this pattern but there are in some), I extract it as an int array with the following format:
[x1, y1, x2, y2, r, g, b, x1, y1, x2, y2, r, g, b....]
So sets of 7 values which represent the start & end of the line and then the r, g, b values of the color. At some point I was attempting to pass this information directly into an Elm port as a Decode.Value and then was intending to decode this internally but I wasn’t sure of the best way to do it. I think I also had another variation which was
[x1, y1, x2, y2, code....] where
code is a string indicating the correct thread to use.
Writing it out now, I guess the first format could be decoded to a
List Int and then processed with
List.Extra.groupsOf 7 and the second format could be decoded to
List Decode.Value and approached some way though I struggle with that. If I could break it down to a
List Decode.Value where each
Decode.Value was internally a JSON list of 5 elements then I could use
Decode.index to pull out the individual parts of it. I guess I could do that grouping on the Typescript side before sending it through the port. Anyway, I didn’t seem clear to me but that doesn’t feel like a huge problem. It is a bit of a specific case. I ended up structured the data into objects on the Typescript side before sending it through in order to make the decoding super easy.