DEV Community

Thai Pangsakulyanont
Thai Pangsakulyanont

Posted on

monetizer: A JS library that manages the monetization meta tag (for Web Monetization API)

What I built

A JavaScript library that manages the <meta name="monetization"> tags. It is designed to be used in single-page JavaScript apps that needs to dynamically monetize different creators.

Design goals:

  • Build a simple API to integrate with any frontend library or framework (with React hooks integration as the main use case).
  • No dependencies.
  • Support single page apps with dynamically-changing payment pointer.
  • Support probabilistic revenue sharing.
  • Has TypeScript type definitions bundled.

The main use case is to allow Single-Page Apps to monetize creators dynamically based on the content displayed. For example, an article website may want to monetize the article’s author when a user is reading their articles, while falling back to monetizing the website owner on other pages. This library allows such use cases to be implemented declaratively.

Submission Category:

Foundational Technology

Demo

Vanilla JS usage

// Starts monetizing
const stopMonetizing = monetize('$dt.in.th')

// Stops monetizing
stopMonetizing()
Enter fullscreen mode Exit fullscreen mode

Under the hood, when you call monetize, it adds a new monetization candidate to an internal candidate pool. When the candidate pool is changed, the library picks a winning candidate and adds/updates/removes the meta tag accordingly.

Likewise, when you call stopMonetizing, the previously added candidate is removed from the pool, which results in the meta tag being removed (if it is the only candidate).

This concept of a “candidate pool” will come in useful when we want to do probabilistic revenue sharing or

React usage

The API has been designed specifically so that it snaps neatly into a useEffect call.

function App() {
  // Start monetization on mount, stop on unmount.
  useEffect(() => monetize('$dt.in.th'), [])
  return (
    <Layout>
      <Main />
    </Layout>
  )
}
Enter fullscreen mode Exit fullscreen mode

Vue usage

export default {
  mounted() {
    this.stopMonetization = monetize('$dt.in.th')
  },
  beforeDestroy() {
    if (this.stopMonetization) {
      this.stopMonetization()
    }
  },
}
Enter fullscreen mode Exit fullscreen mode

Probabilistic revenue sharing

If multiple monetization calls are active, it uses probabilistic revenue sharing to divide the revenue.

// 50% chance
monetize('$dt.in.th')

// 50% chance
monetize('$twitter.xrptipbot.com/bemusegame')
Enter fullscreen mode Exit fullscreen mode

You can specify a weight (default weight is 1).

// 33% chance
monetize({ content: '$dt.in.th', weight: 1 })

// 67% chance
monetize({ content: '$twitter.xrptipbot.com/bemusegame', weight: 2 })
Enter fullscreen mode Exit fullscreen mode

Prioritization

You can also specify the priority. Higher priority wins.

This essentially lets you implement the main use case aforementioned in the beginning.

function App() {
  // Fallback monetization candidate with a default priority of 0
  useEffect(() => monetize('$dt.in.th'), [])
  return (
    <Layout>
      <Main />
    </Layout>
  )
}

function Article(props) {
  const paymentPointer = props.author.paymentPointer

  // Send micropayment to article’s author while viewing author’s article
  // A priority of 1 takes precedence over a priority of 0.
  useEffect(() => monetize({ content: paymentPointer, priority: 1 }), [])

  return ...
}
Enter fullscreen mode Exit fullscreen mode

Priority ties are probabilistic-revenue-shared.

function Article(props) {
  const paymentPointer = props.author.paymentPointer

  // 50-50 revenue sharing between article author and site developer
  useEffect(() => monetize({ content: '$dt.in.th', priority: 1 }), [])
  useEffect(() => monetize({ content: paymentPointer, priority: 1 }), [])
}
Enter fullscreen mode Exit fullscreen mode

This library is being used in an open-source web-based rhythm game I develop, Bemuse. If you look in the page’s source code you will not see the monetization meta tag, but it is later added in via JavaScript.

Link to Code

GitHub logo dtinth / monetizer

A monetization meta tag manager.

monetizer

A monetization meta tag manager.

import { monetize } from 'monetizer'
Enter fullscreen mode Exit fullscreen mode

Extracted from the Bemuse monorepo into a standalone repository.

Project goals

  • Simple API to integrate with any frontend library or framework.
  • No dependencies.
  • Support single page apps.
  • Support probabilistic revenue sharing.
  • Has TypeScript type definitions bundled.

Vanilla JS usage

// Starts monetizing
const stopMonetizing = monetize('$dt.in.th')

// Stops monetizing
stopMonetizing()
Enter fullscreen mode Exit fullscreen mode

React usage

function App() {
  useEffect(() => monetize('$dt.in.th'), [])
  return <Layout><Main /></Layout>
}
Enter fullscreen mode Exit fullscreen mode

Probabilistic revenue sharing

If multiple monetization calls are active, it uses probabilistic revenue sharing to divide the revenue.

// 50% chance
monetize('$dt.in.th')

// 50% chance
monetize('$twitter.xrptipbot.com/bemusegame')
Enter fullscreen mode Exit fullscreen mode

You can specify a weight (default weight is 1).

// 33% chance
monetize({ content: 
Enter fullscreen mode Exit fullscreen mode

How I built it

I originally built the monetizer package for use with the open-source web-based rhythm game Bemuse. So, I originally implemented this package right into the Bemuse monorepo. This is beneficial because:

  • My initial goal was to ship the monetization meta tag to production as quickly as possible, while at the same time, also ending up with an npm package that can be reused in other projects.
  • I don’t have to set up a new build pipeline, and the package can benefit from using existing CI/CD pipeline and linter setup.
  • If the game needs extra features from the library, it can be updated without requiring version bumps across multiple repositories.

Add web monetization meta tag. #671

Basic idea of how it works

  • A Coil membership costs $5 per month (flat membership fee) and requires a browser extension.
  • Creators can put a meta tag on their website.
  • Every 1 second, the Coil extension pays the developer $0.0001 per second spent on that website via interledger to that address.

How does this affect our users

Nothing. The Bemuse game will work exactly the same regardless of whether or not they are a Coil member. There is no behavior difference. Coil members, however, automatically support this game just by playing it.

A future iteration of this feature may allow music server maintainers and artists to get monetized while their song is being played.

Changelog

Added a passive web monetization using the upcoming open standard, Web Monetization API, to see if this can potentially help offset server costs. If you are a Coil member, your membership will help support the server costs. As for Bemuse, nothing will go behind a monetization-wall. The game will remain completely free without any ads.

With that, I released a new version of the game. With the library integrated into the game and shipped into production, the original goal has been accomplished. I can now shift my focus to just this library and think about its future.

The monetizer package is very generic, and has a potential to be evolve independently of the game, unlike other packages in this monorepo which are more likely to evolve alongside the game. Having realized this, I decided to extract the library into its own standalone repository.

I used TSDX to bootstrap a new TypeScript package. Since this library has no dependencies and also follows standard TypeScript project structure from the beginning, extracting it into a standalone repository turns out to be straightforward. TSDX also generate a GitHub Actions workflow file, so the library has testing right out of the box.

Additional Resources/Info

Top comments (0)