We will be undergoing planned maintenance on Oct 7th 6:00AM UTC / Oct 7th 2:00AM ET

Inspiration

Inspired by r/place, Cospray is a common canvas for all users to paint on surfaces throughout the real world. Sidewalks and pillars become shared, living, and collaborative canvases. Strangers in your neighbourhood, campus, or classroom can now create art together, and in real time.

What it does

Cospray is a spatial AR collaborative "graffiti" app anchored to real-world geolocation coordinates.

How we built it

Cospray is an Expo/React Native app (SDK 57, TypeScript, Hermes) wrapped around a native AR module we wrote. Swift against ARKit for iOS, and a parallel ARCore implementation in Kotlin for Android.

List of Expo Technologies

  • expo-sensors — DeviceMotion + Magnetometer sensor fusion for camera pose and physical shake-to-charge.
  • expo-camera — CameraView for the non-AR fallback painter, useCameraPermissions everywhere.
  • expo-audio — looping hiss with live volume/pitch tied to spray strength; plays in silent mode, mixes with music.
  • expo-haptics, expo-brightness, expo-keep-awake — tactile feedback on every action, screen to 100% while painting, never sleeps mid-stroke.
  • expo-location — sub-metre GPS watch (BestForNavigation, 0.5 m) anchors pieces to the world; watchHeadingAsync for compass bearing.
  • expo-notifications — local "Hot spot nearby" banners, with a hand-written config plugin (withMod on the finalized iOS step) that strips the push entitlement so the build signs without Push capability.
  • expo-file-system (new File/Paths API) — downloads/uploads AR world maps (File.downloadFileAsync) so pieces reappear on the exact wall for the next person.
  • expo widget kit extension — used for both the Android and iOS Widgets
  • expo-keep-awake — keeps the screen awake while painting

The anchoring of the paint on real walls: Each wall has a transparent 5m × 5m quad attached to an ARAnchor, which has the CoreGraphics Canvas as its texture. The strokes are made in the anchor’s local coordinate system, meaning that ARKit controls the pose and the paint will stay in place as you move around or approach from an angle.

After you paint, the session's ARWorldMap is uploaded to Supabase Storage, keyed by canvas, alongside every stroke's anchor id and transform. Walk back (within 15m, due to GPS limitations), return, and ARKit relocalizes against it.

Controls: Use the volume buttons to alternate between two different spray colours, and shake to charge up your meter. Paint is a currency that depletes and regenerates, and intuitive controls make the app easy to understand for new users.

Challenges we ran into

iOS terminated the app after eight seconds at every launch. Each anchor in a wall uses an independent texture, and painting for one day a wall made the rebuild at every launch exceed what iOS allocated to the foreground app. We reduced how many wall histories get rebuilt and decreased texture resolution by half.

The paint sync would stop multiple times. Every stroke coming back was violating foreign key constraint, but there was no failed insert of canvas recorded because it never existed. However, the cache had walls from a Supabase project that was configured earlier, so painting joined a wall with id unknown to the database.

Assigning a canvas/grid to surfaces became a significant challenge, with tradeoffs arising with vertical planes (such as walls) and horizontal planes (such as a desk).

Accomplishments that we're proud of

  • Paint that actually holds its wall. Quads re-anchor onto real planes the moment ARKit confirms them, so paint stays flat on the surface from any angle instead of floating at 45°.
  • A second phone sees your piece on the same wall, via uploaded world maps.

What we learned

In AR, memory is the budget. Every anchored surface costs a texture, and a wall someone has painted all day will quietly take the app past what iOS allows.

Compatibility between Android and iOS is significantly hard using native Kotlin and Swift, but with Expo we were able to easily switch between the two (as well as a web app).

What's next for Cospray

  • Tiled texture atlas so long murals don't seam across 5m quads, and many walls cost less memory.
  • Timelapse of a drawing being made: we already store every stroke with a timestamp, so replaying a wall's history is mostly a UI.

Built With

Share this project:

Updates

Submission history