npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@remix-gg/game-sdk

v0.3.0

Published

The Farcade SDK enables game developers to integrate their HTML5 games with the Remix platform through a simple messaging interface.

Downloads

132

Readme

Farcade SDK

The Farcade SDK enables game developers to integrate their HTML5 games with the Remix platform through a simple messaging interface.

Installation

Install the SDK using your preferred package manager:

npm install @farcade/game-sdk
# or 
bun install @farcade/game-sdk
# or
yarn add @farcade/game-sdk
# or
pnpm add @farcade/game-sdk

When you upload game code to Remix, a script tag to reference the SDK will automatically be added to your game code. The SDK will be accessible from window.FarcadeSDK.

Example Implementation

import type { GameState, Player, FarcadeSDK } from '@farcade/game-sdk'

class MyGame {
  sdk: FarcadeSDK
  player: Player
  gameState: GameState | null = null
  isMuted: boolean = true

  // some example values that correlate to items purchased by boosting
  canSaveGame: boolean = false
  hasSuperSkin: boolean = false

  constructor() {
    // attach the sdk to the Game class to we can reference it easily
    this.sdk = window.FarcadeSDK

    // Setup event listeners
    this.setupEventListeners()

    // Initialize your game
    this.initialize()
  }

  private async setupEventListeners() {
    // Listen for play again events
    this.sdk.onPlayAgain(() => {
      this.resetGame()
    })

    // Listen for mute state changes
    this.sdk.onToggleMute((data) => {
      this.isMuted = data.isMuted
    })

    // Listen for purchase completions (optional)
    this.sdk.onPurchaseComplete(() => {
      this.checkPurchasedItems()
    })
  }

  private async initialize() {
    // wait for the sdk to get game information from Remix
    await this.sdk.ready()

    // get information the game uses from the SDK
    this.player = this.sdk.player
    this.gameState = this.sdk.gameState

    // check which items the user has already purchased
    this.checkPurchasedItems()
    
    //... start the game logic
  }

  private checkPurchasedItems() {
    // check the identifier for each item to see if the user has purchased it

    if (this.sdk.hasItem('save-game')) {
      this.canSaveGame = true
    }

    if (this.sdk.hasItem('super-skin')) {
      this.hasSuperSkin = true
    }
  }

  private async purchaseItem(itemId: string) {
    // trigger the boost UI and then check for new items after
    await this.sdk.purchase({ item: itemId })
    this.checkPurchasedItems()
  }

  private gameOver(finalScore: number) {
    //
    this.sdk.singlePlayer.actions.gameOver({ score: finalScore })
  }

  private saveGame() {
    // return unless the user has purchased the 'save-game' item
    // see checkPurchasedItems
    if (!this.canSaveGame) return

    // push game state updates to save progress for the user
    const gameState: GameState = this.getCurrentGameState();
    this.sdk.singlePlayer.actions.saveGameState({ gameState })
  }

  private triggerHapticFeedback() {
    this.sdk.hapticFeedback()
  }
}

API Reference

Properties and Getters

sdk.ready(): Promise<GameInfo>

This function will return when the SDK has recieved all the game information from Remix. You should await the call to this function before getting info from the SDK (sdk.player, sdk.gameState, etc)

sdk.isReady: boolean

Check if the SDK has received game info and is ready. More often, you will just await the call to sdk.ready(), but you may use this value if you prefer.

sdk.player: Player | undefined

Get the current player object.

sdk.players: Player[] | undefined

Get the list of all players in the game. This is useful for multiplayer games. In multiplayer games, it is essential that you use the players array so you can report scores with the associated player ids and notify players it is there turn.

sdk.gameState: GameState | null | undefined

Get the current game state. This will be null when there is no existing GameState for this game and user.

sdk.purchasedItems: string[]

Get the list of item IDs that the current player has purchased. This is a read-only property that reflects the purchasedItems array from the current player's GameInfo.

sdk.hasItem(item: string): boolean

Check if the current player has purchased a specific item.

Parameters:

  • item: The item identifier to check (string)

Returns:

  • true if the player has purchased the item, false otherwise

sdk.gameInfo: GameInfo | undefined

Get the current GameInfo object. This is all the data passed into the game from Remix. It is recommended that you use the other functions to access the specific data you need instead of pulling data out of this large object.

sdk.hapticFeedback()

Call this method to trigger haptic feedback on supported devices. Use this for important game events like collisions, achievements, or other significant player interactions.

Best Practices for Haptic Feedback

  • Use haptic feedback sparingly to avoid overwhelming the player
  • Reserve haptic feedback for meaningful interactions and achievements
  • Consider using it for:
    • Collisions with obstacles
    • Collecting power-ups
    • Completing levels
    • Achieving high scores
    • Important game events

sdk.purchase({ item: string }) => Promise<{ success: boolean }>

Call this function to initiate a purchase for an in-game item. This method sends a purchase request to the Remix platform and returns a promise that resolves with the purchase result. Make sure the item identifier matches an item you have added to your game using the Remix web app.

Parameters:

  • item: The identifier of the item to purchase (string)

Returns:

  • A promise that resolves with { success: boolean } indicating whether the purchase was successful

The purchase flow works as follows:

  1. Call purchase({ item: 'item-id' }) to initiate the purchase
  2. The platform handles the purchase transaction (payment processing, etc.)
  3. The promise resolves with { success: true } if the purchase succeeds, or { success: false } if it fails
  4. You can also listen for purchase_complete events using sdk.onPurchaseComplete() for additional handling

Important: The purchasedItems array in GameInfo will be automatically updated after a successful purchase, and you can check item ownership using sdk.hasItem(item) or sdk.purchasedItems.

Single Player Actions

sdk.singlePlayer.actions.gameOver({ score: number })

Call this method when the game is over to report the final score.

Parameters:

  • score: The player's final score (number)

sdk.singlePlayer.actions.saveGameState({ gameState: Record<string, unknown> })

Call this method when you wish to persist game state for the user. This will enable users to save their progress in a game between play sessions.

Multi-Player Actions

Muliplayer has the same actions as singlePlayer with 2 updates and 1 addition. It is important to note that YOU MUST use the gameInfo data from the response to ready() in order to accurately report scores with player ids when the game ends.

sdk.multiplayer.actions.gameOver({ scores: { playerId: string; score: number }[] })

Call this method with the game is over to report the final scores.

Parameters:

  • scores: The playerId and score for each player in the game.

sdk.multiplayer.actions.saveGameState({ gameState: Record<string, unknown>; alertUserIds: string[] })

Call this method when a player action should update the shared game state for all players. If your game has a concept of turns as most do, you must include the alertUserIds list with the id(s) of the player(s) that should be alerted it is their turn.

Parameters:

  • gameState: A Record object containing your game state. The shape of the game state is open to the game creator's discretion.
  • alertUserIds: A list of player ids to alert on this game state update. Typically this will be a list of length 1 containing the id of the player who is next to act, but different games may need to alert multiple players.

sdk.multiplayer.actions.refuteGameState({ gameStateId: string })

Call this method when your game client has determined an incoming game state is invalid. It is not strictly required that your game validates game state updates, but it should. Putting game state validation logic in your game client means that bad actors attempting to push unfair updates are invalidated by good actors running valid implementations of your game.

Parameters:

  • gameStateId: The id from the game_state_updated event that the game client is refutting.

sdk.multiplayer.actions.purchase({ item: string }) => Promise<{ success: boolean }>

Same as the single player purchase method. Call this method to initiate a purchase for an in-game item in multiplayer games. See the single player purchase documentation above for details.

Here is an example function that handles update and refute game state for a Chess game:

  handleGameStateUpdate({ gameState }) {
    if (!gameState) return;

    const { id, gameState: { moves } } = gameState;

    try {
      this.chess.reset();
      if (moves?.length > 0) {
        for (const move of moves) {
          this.chess.move(move);
        }
      }
      this.renderBoard();
      this.updateStatus();
      this.updateMoveHistory();
      this.updateCapturedPieces();
    } catch (error) {
      // game state is invalid, refuting this game state update
      this.sdk.multiplayer.actions.refuteGameState({ gameStateId: id });
    }
  }

Events

The SDK provides both generic event listeners and specific typed listener methods. We recommend using the specific listener methods for better type safety and clarity.

Event Listeners

sdk.onPlayAgain(callback: () => void)

Register a callback function that is called when the player wants to play again.

sdk.onToggleMute(callback: (data: { isMuted: boolean }) => void)

Register a callback function that is called when receiving mute state changes.

Parameters:

  • callback: Function that receives { isMuted: boolean } indicating the current mute state
sdk.onGameStateUpdated(callback: (data: { id: string, gameState: Record<string, unknown> } | null) => void)

Register a callback function that is called when there is an update to the game state. This is essential for multiplayer games to receive state updates from other players.

Parameters:

  • callback: Function that receives the game state update data, or null if the game state is cleared
sdk.onGameInfo(callback: (data: GameInfo) => void)

Register a callback function that is called when game information is received. This is typically called automatically when the game initializes, but you can listen for updates.

Parameters:

  • callback: Function that receives the GameInfo object
sdk.onPurchaseComplete(callback: (data: { success: boolean }) => void)

Register a callback function that is called when a purchase completes. This can be used for additional handling beyond the promise returned by purchase(). It returns the same value as awaiting the call to purchase(), but sometimes users may boost your game without your game triggering the purchase function.

Parameters:

  • callback: Function that receives { success: boolean } indicating whether the purchase succeeded

TypeScript Support

The SDK is written in TypeScript and includes full type definitions.

Example Multiplayer Implementation

Multiplayer game development is not yet available to all users, but the SDK supports it. Watch the Remix Discord for announcements about multiplayer game support.

import type { FarcadeSDK, GameState, Player } from '@farcade/game-sdk'

class Game {
  sdk: FarcadeSDK
  gameState: GameState
  player: Player
  players: Player[]

  constructor() {
    this.sdk = window.FarcadeSDK

    // Initialize your game
    this.initialize()

    // Setup event listeners
    this.setupEventListeners()
  }

  private setupEventListeners() {
    // Listen for play again events
    this.sdk.onPlayAgain(() => {
      this.resetGame()
    })

    // Listen for mute state changes
    this.sdk.onToggleMute((data) => {
      this.setMuted(data.isMuted)
    })

    // Listen for game state updates (essential for multiplayer)
    this.sdk.onGameStateUpdated((data) => {
      const { id, gameState } = data
      const newGameStateIsValid = this.validateGameState(gameState);

      if (newGameStateIsValid) {
        this.gameState = gameState
      } else {
        sdk.multiplayer.actions.refuteGameState({ gameStateId: id });
      }
    })

    // Listen for purchase completions (optional)
    sdk.onPurchaseComplete((data) => {
      if (data.success) {
        this.handlePurchaseSuccess()
      }
    })
  }

  private async initialize() {
    await this.sdk.ready()

    this.gameState = this.sdk.gameState
    this.player = this.sdk.player
    this.players = this.sdk.players
  }

  private saveGameState(gameState: Record<string, unknown>, nextTurnPlayerId: string) {
    sdk.multiplayer.actions.saveGameState({
      gameState,
      alertUserIds: [nextTurnPlayerId],
    })
  }

  private gameOver(scores: { playerId: string, score: number }[]) {
    sdk.multiplayer.actions.gameOver({ scores })
  }

  private async purchaseItem(itemId: string) {
    try {
      const result = await sdk.multiplayer.actions.purchase({ item: itemId })
      if (result.success) {
        console.log(`Successfully purchased ${itemId}`)
        // Item is now available via sdk.hasItem(itemId) or sdk.purchasedItems
        // The purchasedItems array is automatically updated for all players
      } else {
        console.log(`Purchase failed for ${itemId}`)
      }
    } catch (error) {
      console.error('Purchase error:', error)
    }
  }
}

License

MIT