When designing a new SDK for Firefly Zero, we follow these principles:
- Reduce type convertions. Try to use the same integer type everywhere. WebAssembly is 32-bit system, so in Rust it will be
i32. In Go,lenand slice indexing useint, so that’s the best “default” type. If the language supports generic constructors, consider using it to automatically cast 8-bit and 16-bit integers to the default integer type. Using 8-bit or 16-bit integer in some types, likePad, would potentially reduce the size of heap allocations, but most of the time these definitions never leave the stack, and in WebAssembly on the stack everything takes at least 32 bit. - Provide type casts. If the language provides custom type casting, define it for converting
PadtoPoint(and back),PadtoDPad,CanvastoImage,StyletoLineStyle(and back), etc. - Provide custom types. While
load_filecan return a raw byte array anddraw_imagecan accept any byte array, it’s good to provide customFileandImagetypes (and other custom types too whenever is possible). Even if the language doesn’t provide nominal type safety, a definition likeenemy: Imageis much more descriptive thanEnemy: []byte. - Host calls are functions. You can optionally provide
Linetype withdrawmethod for the user’s convenience. However, there must be always adraw_linefunction, like for any other WebAssembly host call. The idea is that host calls are side-effects and that’s how we make it explicit. - Keep the host functions names. If the host function is called
draw_line, the SDK function wrapping it must be calleddraw_line(ordrawLine, orDrawLine) too. Don’t call itrender_lineor anything else. The called host function names are visible in the runtime error messages and infirefly_cli inspect, so it should be easy for the user to map it to the SDK function calls in their code. - Keep consistent type names. Use the following type names:
- Grpahics:
Angle,Canvas,Color,Font,Image,LineStyle,Point,RGB,Size,Style,SubImage. - FS:
File. - Input:
Pad,DPad,Buttons. - Net:
Peer,Peers,Stash. - Stats:
Badge,Board,Progress. - Shapes:
Shape,Line,Rect,RoundedRect,Circle,Ellipse,Triangle,Arc,Sector.
- Grpahics:
- Follow the language style guides. Follow the naming convention and all the best practice. If there are several competing standards, pick the most commonly used one. Use all possible linters and code formatters.
- Keep modules flat. There should be only 2 public modules:
firefly.audiowith everything related to audio andfireflywith everything else (andsudoif your SDK provides sudo functions). Even though the host runtime defines modules likegraphics,misc, and more, the SDKs’ public API shouldn’t reflect that structure. Make it fast and easy for users to find and import all they need. - Avoid allocations. It should be possible to use SDK to write allocation-free apps. For example, in Go,
ReadPadreturns(Pad, bool)instead of*Padso it doesn’t escape on the heap. In case of reading a file, most users will want it the file be automatically allocated with the correct size, so you should make both possible. For example, the Go version ofLoadFileaccept an optional buffer and if it isnil, a new one is automatically allocated. Or in Rust version, there are two implementations:load_fileandload_file_buf. - Optimize (for size). You should provide a simple to use API but it must never sacrifice binary size or performance. If someone wants to push the limits of what Firefly Zero can do, they should be able to do that without bypassing the official SDK. However, in many cases, the compiler is smart enough to optimize out unused code branches, so always validate your assumptions.
- Keep the arguments order. The host functions follow strict rules for arguments order. In particular, drawing functions always accept
Pointfirst andStylelast. Your wrappers should follow the same order. - Use 32-bit float. 32-bit float arithmetic is much faster on the device than 64-bit one.
- Provide helpers. Don’t stop just at the host function wrappers. Provide everything that a typical game might need. For example, arithmetic operations for
Point, radial coordinates forPad, or optional integration with a popular 2D math library.