43 releases (8 breaking)
Uses new Rust 2024
| 0.9.4 | Apr 17, 2026 |
|---|---|
| 0.7.7 | Apr 10, 2026 |
| 0.5.8 | Mar 30, 2026 |
| 0.1.2 | Dec 29, 2025 |
| 0.0.1 | May 28, 2025 |
#159 in Graphics APIs
745KB
17K
SLoC
spottedcat
Spottedcat is a lightweight cross-platform 2D/3D game engine built with Rust and wgpu. It provides a simple API for rendering, input, audio, text, and scene management across desktop, web, iOS, and Android. Designed for fast prototyping and creative interactive projects, it aims to stay small, practical, and easy to use.
Warning
ALPHA VERSION: This library is currently in an alpha state. The API is subject to frequent breaking changes and significant refactoring. Use with caution in production environments.
Stability
- Stable-ish core direction:
Context,Spot,Image,Model,Text, andrunare the primary surfaces the crate is trying to converge around. - Still volatile: scene payload internals, shader extension points, platform-specific behavior, audio internals, and lower-level rendering details may change between minor releases.
- Release expectation: until
1.0, minor versions may include breaking API changes, behavior fixes, and platform-specific adjustments. - Production guidance: pin an exact crate version if you ship with
spottedcattoday, and review changelogs before upgrading.
Why spottedcat?
The library is named after the Rusty-spotted cat (Prionailurus rubiginosus), the world's smallest wild cat. Just like its namesake, this library aims to be tiny, agile, and remarkably efficient.
Features
- Simple API: Minimal core types to learn:
Context,Spot,Image,Model,Text, andrun. - GPU-accelerated: Built on wgpu for high-performance rendering.
- 2D & 3D Support: Draw 2D UI and images, or load and render 3D models with PBR materials and skeletal animation.
- 3D Billboards: Easily render 2D textures as 3D billboards that properly depth-sort with 3D objects.
- Instanced Rendering: Draw thousands of identical 3D models (grass, particles, crowds) natively in a single CPU draw call via
draw_instanced. - Custom Shaders: Inject custom WGSL code into the rendering pipeline for both 2D and 3D.
- Image operations: Load from files, create from raw data, extract sub-images.
- Text rendering: Custom font support, text wrapping, and styling (color, stroke).
- Audio support: Play sounds, sine waves, and handle fades/volume.
- Input management: High-level API for Keyboard, Mouse, and Touch events.
- Scene management: Easy switching between game scenes with payload support.
- Resource management: Built-in dependency injection for shared resources.
- Cross-platform: Support for Desktop, Web (WASM), iOS, and Android.
Quick Start
Add to your Cargo.toml:
[dependencies]
spottedcat = "0.9.2"
By default, only the 2D core is enabled for maximum efficiency. To use 3D models or asset loaders (PNG/GLTF), enable the corresponding features:
[dependencies]
spottedcat = { version = "0.9.4", features = ["model-3d", "utils", "gltf", "effects", "sensors"] }
Basic Example
use spottedcat::{Context, Spot, Image, DrawOption, Pt, WindowConfig};
use std::time::Duration;
struct MyApp {
image: Image,
}
impl Spot for MyApp {
fn initialize(ctx: &mut Context) -> Self {
// Create an image from raw RGBA8 data
let rgba = vec![255u8; 64 * 64 * 4]; // Red square
let image = Image::new(ctx, Pt::from(64.0), Pt::from(64.0), &rgba)
.expect("Failed to create image");
Self { image }
}
fn update(&mut self, _ctx: &mut Context, _dt: Duration) {
// Handle logic here
}
fn draw(&mut self, ctx: &mut Context, screen: Image) {
let (w, h) = spottedcat::window_size(ctx);
// Draw image at center
let opts = DrawOption::default()
.with_position([w / 2.0, h / 2.0])
.with_scale([2.0, 2.0]);
screen.draw(ctx, &self.image, opts);
}
fn remove(&mut self, _ctx: &mut Context) {}
}
fn main() {
spottedcat::run::<MyApp>(WindowConfig {
title: "SpottedCat Example".to_string(),
..Default::default()
});
}
Built-in Intro Scene
If you want the game to show a branded startup intro before entering your own
scene, wrap your root scene with OneShotSplash<T>:
use spottedcat::{Context, OneShotSplash, Spot, WindowConfig, run};
struct MyGame;
impl Spot for MyGame {
fn initialize(_ctx: &mut Context) -> Self {
Self
}
fn update(&mut self, _ctx: &mut Context, _dt: std::time::Duration) {}
fn draw(&mut self, _ctx: &mut Context) {}
}
fn main() {
run::<OneShotSplash<MyGame>>(WindowConfig::default());
}
The default intro uses a font-free pixel-art Rusty-spotted cat logo and automatically switches to your main scene after a short delay. Players can also skip with Space, Enter, mouse click, or touch. The splash is shown once per process, so Android surface restoration resumes directly into your game.
AI Assistant Guide
For comprehensive guidance on generating games and working with the spottedcat engine, please refer to the dedicated AI Game Generation Guide.
API Overview
Core Components
Context: Central state for managing draw commands, input, audio, and resources. Hides internal methods to ensure consistency.Spot: Trait defining application lifecycle (initialize,update,draw,remove).Image: GPU texture handle for 2D drawing. Created viaImage::new(ctx, ...)and rendered viatarget.draw(ctx, &image, ...). Use the providedscreeninSpot::drawas the default target.Model: 3D model handle created viaspottedcat::model::create(...)and rendered viaspottedcat::model::*.Text: High-level text structure for 2D layout.Interpolated<T>: Wrapper for game state that provides smooth interpolation between fixed logic updates.DrawOption: Unified configuration for layer, position, rotation, scale, and clipping in 2D.DrawOption3D: Configuration for 3D model placement (position, rotation, scale).
API Style
- Context-based operations (
ctx:*) live at crate top-levelspottedcat::*(for example:register_font,set_window_size,key_down,play_sound), while image creation and drawing live onImagemethods and model creation lives atspottedcat::model::create. - Assets
Forces pending asset rebuild/re-upload to GPU to run immediately with spottedcat::rebuild_assets(ctx).
-
Resource operations stay inside their domains:
Imagemethods for creation, sub-image extraction, and target-based drawing.Texturefor managing underlying GPU textures and creating render targets.spottedcat::model::*for model create/draw/instancing/shader draw (viatarget.draw).spottedcat::text::*for text draw/measure (viatarget.draw).
-
For encoded PNG/JPEG/WebP bytes, enable the
utilsfeature and usespottedcat::utils::image::from_image(...)orfrom_rgba_image(...)after decoding with theimagecrate. These helpers preserve pixel dimensions and derive the default logical size from the currentscale_factor.
Key Systems
- Input: Check keys with
spottedcat::key_down(ctx, ...), mouse withspottedcat::mouse_down(ctx, ...), or getspottedcat::mouse_pos(ctx). - Audio: Play sounds with
spottedcat::play_sound(ctx, ...). - Scenes: Transition between states using
spottedcat::switch_scene::<NewScene>(). - Resources: Share data between systems via
spottedcat::get_resource::<T>(ctx).
Sensors
Enable the sensors feature to access motion and step APIs.
today_step_count(ctx)returns the current day's steps when the platform can provide them.step_detected(ctx)reports whether a new step was observed during the current frame.
Step semantics are intentionally limited to "today" for cross-platform consistency:
- iOS uses
CMPedometerupdates starting from the beginning of the current local day. - Android derives today's steps from
TYPE_STEP_COUNTERwhile the sensor stays registered. - Neither API should be treated as a lifetime or historical total. Historical fitness data belongs in HealthKit or Health Connect integration.
Model 3D
The model-3d feature gates the 3D model stack, including Model, DrawOption3D, custom model shaders, mesh loaders, and lighting. This feature is disabled by default to minimize the engine's footprint when only 2D features are needed.
Performance & Timing
Spottedcat uses a decoupled main loop to ensure consistent game logic across different hardware while maintaining smooth visuals:
- Fixed-Step Update (
UPS):Spot::updateis called at a fixed frequency (defaults to 60Hz). All physics and gameplay logic should happen here. Thedtprovided is constant. - Variable-Rate Draw (
FPS):Spot::drawis called as fast as the display refresh rate or OS allows. - State Interpolation: To prevent "stutter" when FPS and UPS don't match, use the
Interpolated<T>wrapper for game state (like positions). It automatically smooths out values indrawcalls using the engine's internal interpolation factor.
// In your Spot implementation
fn draw(&mut self, ctx: &mut Context, screen: Image) {
// value(ctx) returns the smoothly interpolated position
let pos = self.player_pos.value(ctx);
screen.draw(ctx, &self.player_img, DrawOption::new().with_position(pos));
}
Advanced 2D Features
Automatic Texture Atlasing
For performance efficiency, Spottedcat automatically manages small images (<512px) in a shared internal texture atlas. This reduces GPU state changes and draw calls under the hood without requiring any manual sprite sheet management from the developer.
Custom Shaders
You can register custom WGSL shaders for Image rendering with register_image_shader_desc (2D) and custom full WGSL model shaders with register_model_shader (3D).
For the current 2D Image shader contract, including full WGSL registration, group/binding layout, screen/history inputs, and ShaderOpts mapping, see docs/image-shader.md.
For the current 3D Model shader contract, including group/binding layout and required entry points, see docs/model-shader.md.
To avoid writing full WGSL from scratch, use the built-in template helpers:
spottedcat::image_shader_template()spottedcat::model_shader_template()
The recommended 2D path is spottedcat::ImageShaderTemplate plus spottedcat::register_image_shader_template(...), which exposes explicit shared, vertex_body, and fragment_body slots.
The recommended 3D path is spottedcat::ModelShaderTemplate plus spottedcat::register_model_shader_template(...), which exposes shared and fragment_body slots on top of the engine's fixed model pipeline contract.
3D Shader Layout
Custom 3D model shaders now use full WGSL sources. See docs/model-shader.md for the binding layout, required vertex inputs, and entry-point names.
See examples/image_shader_template.rs and examples/metal_sphere.rs for template-based examples, and examples/advanced_image_shader_full.rs plus examples/advanced_model_shader_full.rs for the advanced full-WGSL path.
Platform Support
Declared support:
-
Desktop: Windows, macOS, Linux.
-
Web: Compile to WASM with
wasm-pack. Seecanvas_idinWindowConfig. -
Android: Integrated with
winit's android-activity. -
iOS: Support for UIKit and native sensor access.
-
WASM example:
examples/wasm/webandexamples/wasm/wasm_demo -
Android example:
examples/android/GameActivityExampleandexamples/android/spottedcat_android_wrapper -
iOS example:
examples/ios/SpottedcatIosSimulatorExampleandexamples/ios/spottedcat_ios_wrapper
Generated outputs for these examples such as target/, .gradle/, pkg/, .xcframework/, and IDE caches are intentionally excluded from version control.
License
This project is licensed under either of:
- Apache License, Version 2.0
- MIT license
at your option.
Dependencies
~16–62MB
~1M SLoC