Skip to content

Embedded application database with peer-to-peer sync

Canvas is an open-source, cryptographically authenticated version of Firebase that lets you write entire applications inside your frontend.

Use it to build local-first applications without depending on a central server. Or, use it to build open protocols that anyone can interoperate with.

Runs on browser, desktop, or mobile
Cross-database compatibility
Realtime sync
Live subscriptions
Transactional mutations
Database editor
MIT License
Embedded CRDTsSoon
Private DataSoon
Email LoginSoon

NPM VersionGitHub starsNPM Downloads


Quick start

ts
import { Canvas } from "@canvas-js/core"

export const models = {
  messages: {
    id: "primary",
    text: "string",
    $indexes: ["id"]
  }
}

export const actions = {
  createMessage: async (text) => {
    const { address, db, id } = this
    await db.set("messages", { id, text })
  }
}

const app = await Canvas.initialize({
  topic: "example.xyz",
  contract: { models, actions },
})

app.actions.createMessage("Who up?")
ts
import { Contract } from "@canvas-js/core"

export const Chat extends Contract {
  static models = {
    messages: {
      id: "primary",
      content: "string",
      address: "string",
    }
  }

  async createMessage(content: string) {
    db.create("messages", {
      content,
      address: this.address
    })
  }
}

const app = await Chat.initialize({
  topic: "example.xyz",
  contract: Chat,
})

app.actions.createMessage("Hello world!")
ts
import { useCanvas, useLiveQuery } from "@canvas-js/hooks"
import { Chat } from "./contract.ts"

const wsURL = process.env.SERVER_WSURL || null

export const App = () => {
  const { app, ws } = useCanvas(wsURL, {
    topic: "example.xyz",
    contract: Chat,
  })
  const items = useLiveQuery(app, "messages")

  return (<div>
    <ComposeBox
      onSend={app.actions.createMessage}
    />
    <ItemView content={items}></ItemView>
  </div>)
}
ts
// In contract.ts:
export const models = {
  messages: {
    id: "primary",
    text: "string"
  }
}

export const actions = {
  createMessage: ({ text }) => {
    const { address, db, id } = this
    db.set("messages", { id, text })
  }
}

// From the command line:
$ canvas run contract.ts --topic example.xyz 

Every application is defined as a contract, a virtual backend with models and actions.

  • Models define your database schema.
  • Actions define mutations that users can make to the database, like API routes.

Because actions are embedded in the database, every peer can validate the history of your application, without a central server.

Now, run your app from the command line:

sh
canvas run contract.ts --topic example.xyz
[canvas] Bundled .ts contract: 4386 chars
[canvas] Serving HTTP API: ...

This starts a peer that you can connect to from your browser. By default, it will also connect to other servers on the application's topic.

Read on to learn how to authenticate users, upgrade your app, or deploy to a server.


About Canvas

Canvas is based on several years of research on a new architecture for distributed web applications. It builds on work from projects including IPFS, OrbitDB, and other peer-to-peer databases.

We've published some of our technical presentations here:

The current release of Canvas is an early developer preview that we are using in a limited set of production pilots. This first release is recommended for protocolized applications - applications with public data that anyone can permissionlessly interoperate with.

In 2025, we are working to expand the categories of applications that we can support, and provide support to developers building on the system. For more information, please reach out on Discord.

© 2025 Canvas Technologies, Inc.