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 🙏

© 2026 – Pkg Stats / Ryan Hefner

syncternet-demo

v1.0.7

Published

Syncternet demo page showcasing the Syncternet NPM package. This project is also a good starting point to develop Syncternet plugins.

Readme

Syncternet - Demo Page

This repository is a demo page that uses the syncternet package. If you want to test the package, make changes to the package or create and test your own plugins, do so using the project found at "./dev_modules/syncternet" at this same level. The demo page will reflect all your changes inside the "dev_modules" folder.

Syncternet allows you to interact with other users on the same page. It is a real-time chat and interaction platform that allows you to share your browsing experience with others.

Test the demo

  1. Go to www.syncternet.com.
  2. Open the page in your phone and/or or in another browser (use a Private Mode to allow creating a new session).
  3. See other people around the page and interact with them.

Add Syncternet to your own project

  1. Install the package with npm install syncternet.
  2. Load the script in the front-end with <script src="/syncternet/client"></script>, this route is injected to the Express app when the module is loaded.
  3. Initialize the server with const syncternet = require('syncternet') and syncternet.init(app), where app is your Express app.

Develop your own plugins

  • npm install
  • npm run dev

The dev command should build the front-end automatically, otherwise you may need to run a build* command

How it works

Server

Users are stored in the users global variable. This variable maps UUIDs to usernames. Clients are never expected to know other UUID's than themselves. Example structure:

const users = {
  "jumping-dog-123": "johnDoe789",
	"sleeping-cat-321": "janeDoe987",
    // ... and so on for other users
}

A matching UUID and username is what makes an user an authenticated one. For this reason UUIDs should never be shared to the client. Public information about the each user for each plugin is stored in public. For example:

const public = {
  party: {
    "jumping-dog-123": {
      pos: {
        x: 13,
        y: 24,
      },
    },
    "sleeping-cat-321": {
      pos: {
        x: 88,
        y: 75,
      },
    },
    // ... and so on for other users
  },
  emoticons: {
    // ... plugin data
  },
  // ... and so on for other plugins
}

In the example above there are two users at two different locations on the page. Note: Actual Party plugin positions are not stored in X, Y coordinates since each user has a different screen size.

Private information about each user is stored in private. Not necessarily for secret data. It generally contains information to make the plugin work correctly. Private data is never shared to other users. Example:

const public = {
  party: {
    "jumping-dog-123": {
      secondsIdle: 30,
    },
    "sleeping-cat-321": {
      secondsIdle: 5,
    },
    // ... and so on for other users
  },
  emoticons: {
    // ... plugin data
  },
  // ... and so on for other plugins
}

Plugin definition is found inside the plugins folder. Each folder is a plugin. Three files are defined:

  • template.html: Defines the plugin's HTML.
  • backend.js: Defines init and middleware functions that are executed on the server-side.
  • frontend.js: Defines init and middleware functions that are executed on the client-side.

The init function is executed once when the plugin is loaded.

The middleware function is executed each time a message is sent/received.


When a new server is started the server begins listening to msg event. Each case is described below:

    • "_new" is received: This is a new user. Follow the steps,

      1. Add a new, randomly-generated UUID and username pair to users.
      2. Server sends "_keys|<UUID>|<username>" to the client.
      3. Send initial plugin information like "_plugins|<plugins>".
    • "_continue|<UUID>|<username>" is received: This is supposedly an existing user. Test if users[<UUID>] === <username>. There are 2 possibilities:

  • It does not match: Don't continue and treat the user as a completely new user as described in previous steps.

  • It matches: Plugin information is sent as described in previous steps.

  • In all other cases, plugin information about another user is received and the following steps are taken:

    1. Server receives "party|<UUID>|{<plugin data>}".
    2. public.party[<UUID>] is updated with plugin data.
    3. Get username from users.
    4. Server broadcasts "party|<username>|{<plugin data>}" to all clients including the sender.

Client

Users spin a VUE instance that looks like:

new Vue({
  data: {
    public: {
      // Realtime data, every user has a copy of this
      party: {
        johnDoe123: {
          xpath: "",
          pos: { x: 3, y: 5 },
        },
        janeDoe987: {
          xpath: "",
          pos: { x: 6, y: 8 },
        },
        // ... and so on for other users
      },
      emoticons: {
        // ... plugin data
      },
      // ... and so on for other plugins
    },
    private: {
      // Local data, every user has it own data
      UUID: "jumping-dog-123", // What server sent us
      username: "johnDoe789", // What we chose
    },
  },
  created() {},
  mounted() {},
  methods: {},
})

When a new user loads a crowwwd page we do:

  1. Check if auth is present in localStorage.getItem('crowwwd:auth'). There are two possibilities with different responses:

    • There is no auth data: "_new" is sent to the server as it a completely new user.
    • This is a returning user: "_continue|<UUID>|<username>" is sent.
  2. Begin listening to the msg event. This is when a WS message is received. This may serve to update plugin data or for auth purposes. Each case is described below:

    • "_keys|<UUID>|<username>" is received: Update the UUID and username in data.private.
    • "_plugins|<plugins>" is received: Add all missing HTMLs. Some of them may be already added manually by the developer.
    • In all other cases, plugin information about another user is received and the following steps are taken:
      1. "party|<username>|{<plugin data>}" is received. Note how the UUID is not present.
      2. Update data.public[<username>] with the new information.

In the steps above we checked for localStorage["crowwwd:auth"]. This variable has the structure "<UUID>:<username>". For example: "jumping-dog-123:johnDoe789".

FAQ

...

...

SO resources:

  • https://stackoverflow.com/questions/5100376/how-to-watch-for-array-changes