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

@coffeeandfun/konami-code-detector

v1.0.1

Published

A modern, async-enabled Konami Code detector

Readme

🎮 @coffeeandfun/konami-code-detector

A tiny, modern library that watches for the Konami Code (↑ ↑ ↓ ↓ ← → ← → B A) — or any key sequence you pick — and runs a callback when the user finishes it.

  • 🪶 Zero runtime dependencies
  • 🚀 Ships as native ESM with TypeScript declarations
  • 🧩 Works with React, Vue, Svelte, Angular, plain JavaScript, Chrome extensions — anywhere with a DOM
  • ⚙️ Configurable: custom sequences, cooldowns, one-shot mode, max-attempts, progress events, async callbacks
  • 🧪 Fully tested with Jest + jsdom

📦 Install

npm install @coffeeandfun/konami-code-detector

Requires Node.js 16+ to install. In the browser it works anywhere AbortController and modern classes are supported (Chrome 66+, Firefox 57+, Safari 11.1+, Edge 79+).

⚡ Quick start

import KonamiCode from '@coffeeandfun/konami-code-detector';

const konami = new KonamiCode(() => {
  document.body.classList.add('party-mode');
});

await konami.enable();
// Press ↑ ↑ ↓ ↓ ← → ← → B A — the callback fires 🎉

That's it. Everything else in this README is optional.

🧠 Mental model

You create an instance, give it a callback, call enable(), and the detector listens for keydown events. Every correct key in the sequence fires a progress event. A wrong key resets progress and fires a failed event. When the user finishes the sequence:

  1. Your callback runs (sync or async — both are awaited).
  2. An activated event fires.
  3. A bubbling konamicode CustomEvent is dispatched on your target, so code outside the instance can react too.

If you stop caring, call destroy() and it cleans itself up.

🖼️ What an instance looks like

const konami = new KonamiCode(callback, options);
// →
{
  callback,               // the function you passed
  config: {
    sequence,             // resolved key list
    timeout,              // ms of inactivity before progress resets
    target,               // element the keydown listener is on
    preventDefault,       // whether to preventDefault on the final key
    once,                 // fire at most once?
    debug,                // log state transitions?
    maxAttempts,          // cap on activations
    cooldown,             // ms between activations
  },
  attempts: 0,            // successful activations
  stats: {
    activations: 0,
    attempts: 0,          // failed attempts (wrong keys)
    lastActivated: null,  // ms timestamp of last success
  },
  // plus the methods documented below
}

🧪 Examples by framework

Each example is self-contained and does the same thing: fires a callback when the Konami code is entered. Pick the one that matches your stack.

🟨 Vanilla JavaScript (with a bundler)

import KonamiCode from '@coffeeandfun/konami-code-detector';

const konami = new KonamiCode(() => {
  alert('🎉 You found the secret!');
});

konami.enable();

🌐 Plain HTML, no bundler (ESM CDN)

<!doctype html>
<html>
  <body>
    <h1>Try the Konami code 👀</h1>
    <script type="module">
      import KonamiCode from 'https://esm.sh/@coffeeandfun/konami-code-detector';

      const konami = new KonamiCode(() => {
        document.body.style.background = 'linear-gradient(45deg, #ff006e, #8338ec)';
      });

      konami.enable();
    </script>
  </body>
</html>

⚛️ React (hook version)

import { useEffect, useRef } from 'react';
import KonamiCode from '@coffeeandfun/konami-code-detector';

export function App() {
  const konamiRef = useRef(null);

  useEffect(() => {
    konamiRef.current = new KonamiCode(() => {
      console.log('✨ activated in React!');
    });
    konamiRef.current.enable();

    return () => {
      konamiRef.current?.destroy();
    };
  }, []);

  return <div>Try the Konami code 🎮</div>;
}

⚛️ React (custom hook — reusable)

import { useEffect, useRef } from 'react';
import KonamiCode from '@coffeeandfun/konami-code-detector';

export function useKonamiCode(callback, options) {
  const callbackRef = useRef(callback);
  callbackRef.current = callback;

  useEffect(() => {
    const konami = new KonamiCode(() => callbackRef.current?.(), options);
    konami.enable();
    return () => { konami.destroy(); };
  }, []);
}

// Usage:
function PartyButton() {
  useKonamiCode(() => {
    document.body.classList.add('party-mode');
  });
  return <button>Keep trying… 🕹️</button>;
}

🟢 Vue 3 (Composition API)

<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import KonamiCode from '@coffeeandfun/konami-code-detector';

const konami = ref(null);

onMounted(() => {
  konami.value = new KonamiCode(() => console.log('🎉 activated in Vue!'));
  konami.value.enable();
});

onUnmounted(() => {
  konami.value?.destroy();
});
</script>

<template>
  <p>Try the Konami code 🎮</p>
</template>

🟢 Vue 3 (reusable composable)

// composables/useKonamiCode.js
import { onMounted, onUnmounted, ref } from 'vue';
import KonamiCode from '@coffeeandfun/konami-code-detector';

export function useKonamiCode(callback, options) {
  const instance = ref(null);

  onMounted(() => {
    instance.value = new KonamiCode(callback, options);
    instance.value.enable();
  });

  onUnmounted(() => {
    instance.value?.destroy();
  });

  return instance;
}
<script setup>
import { useKonamiCode } from './composables/useKonamiCode';

useKonamiCode(() => {
  document.body.classList.add('party-mode');
});
</script>

🔺 Svelte 4 / 5

<script>
  import { onMount, onDestroy } from 'svelte';
  import KonamiCode from '@coffeeandfun/konami-code-detector';

  let konami;

  onMount(() => {
    konami = new KonamiCode(() => console.log('🎉 activated in Svelte!'));
    konami.enable();
  });

  onDestroy(() => {
    konami?.destroy();
  });
</script>

<p>Try the Konami code 🎮</p>

🅰️ Angular

import { Component, OnDestroy, OnInit } from '@angular/core';
import KonamiCode from '@coffeeandfun/konami-code-detector';

@Component({
  selector: 'app-root',
  template: '<p>Try the Konami code 🎮</p>',
})
export class AppComponent implements OnInit, OnDestroy {
  private konami?: KonamiCode;

  ngOnInit() {
    this.konami = new KonamiCode(() => console.log('🎉 activated in Angular!'));
    this.konami.enable();
  }

  ngOnDestroy() {
    this.konami?.destroy();
  }
}

▲ Next.js (App Router — client component)

'use client';

import { useEffect, useRef } from 'react';
import KonamiCode from '@coffeeandfun/konami-code-detector';

export function KonamiListener() {
  const ref = useRef<KonamiCode | null>(null);

  useEffect(() => {
    ref.current = new KonamiCode(() => {
      console.log('🎉 activated on the client');
    });
    ref.current.enable();

    return () => { ref.current?.destroy(); };
  }, []);

  return null;
}

Drop <KonamiListener /> in your root layout. The 'use client' directive keeps it out of SSR where there's no document.

🧩 Chrome extension (content script)

import KonamiCode from '@coffeeandfun/konami-code-detector';

const konami = new KonamiCode(async () => {
  const response = await chrome.runtime.sendMessage({ type: 'KONAMI_ACTIVATED' });
  console.log('unlocked 🔓', response);
});

konami.enable();
// background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'KONAMI_ACTIVATED') {
    console.log('🎮 Konami activated on', sender.tab?.url);
    sendResponse({ success: true });
  }
});

🧪 Node.js (headless testing)

The detector works without a DOM if you pass target: null and use the built-in triggerKey / triggerSequence helpers.

import KonamiCode, { SEQUENCES } from '@coffeeandfun/konami-code-detector';

const konami = new KonamiCode(() => console.log('🎉 activated'), { target: null });
await konami.enable();
await konami.triggerSequence(); // → logs "🎉 activated"

🍳 Recipes

Real patterns people use this for.

🎨 Party mode (add a CSS class)

const konami = new KonamiCode(() => {
  document.body.classList.add('party-mode');
});
konami.enable();

🐛 Hidden debug panel (custom sequence)

import KonamiCode, { SEQUENCES } from '@coffeeandfun/konami-code-detector';

const konami = new KonamiCode(
  () => {
    document.querySelector('#debug-panel').hidden = false;
    localStorage.setItem('debugMode', 'true');
  },
  { sequence: SEQUENCES.debug, once: true },
);

konami.enable();
// Type D-E-B-U-G to reveal the panel, once per page load.

📊 Progress bar

const konami = new KonamiCode();
const bar = document.querySelector('#konami-progress');

konami
  .on('progress', ({ percentage }) => { bar.style.width = `${percentage}%`; })
  .on('failed', () => { bar.style.width = '0%'; })
  .on('activated', () => { bar.style.width = '100%'; });

konami.enable();

⏱️ Cooldown between activations

const konami = new KonamiCode(fireConfetti, { cooldown: 5000 });
await konami.enable();
// Subsequent activations inside 5s are silently dropped.

🔂 Fire only once

const konami = new KonamiCode(unlockAchievement, { once: true });
await konami.enable();
// After the first activation, the instance disables itself automatically.

🧭 Track analytics on activation

const konami = new KonamiCode();

konami.on('activated', ({ timestamp, activations }) => {
  analytics.track('konami_code_activated', { timestamp, activations });
});

konami.enable();

👂 Listen from outside the instance (DOM event)

const konami = new KonamiCode();
konami.enable();

document.addEventListener('konamicode', (event) => {
  console.log('activated at', event.detail.timestamp);
  console.log('stats:', event.detail.stats);
});

🔐 Admin-only unlock (async check)

const konami = new KonamiCode(async () => {
  const user = await fetchCurrentUser();
  if (user.role === 'admin') {
    showAdminPanel();
  }
});

konami.enable();

🎚️ Multiple codes, one page

const debugMode = new KonamiCode(enableDebug, { sequence: SEQUENCES.debug });
const godMode   = new KonamiCode(enableGodMode, { sequence: ['g', 'o', 'd'], cooldown: 5000 });
const reset     = new KonamiCode(resetApp, { sequence: ['r', 'e', 's', 'e', 't'], once: true });

await Promise.all([debugMode.enable(), godMode.enable(), reset.enable()]);

📚 API reference

Constructor

new KonamiCode(callback?, options?)

| Parameter | Type | Default | Description | | ---------- | ---------- | ----------------- | ------------------------------------------------ | | callback | Function | defaultCallback | Runs when the sequence completes. May be async. | | options | Object | {} | See below. |

Options

| Option | Type | Default | Description | | ---------------- | ------------- | ------------------- | ----------------------------------------------------------------------- | | sequence | string[] | SEQUENCES.classic | Keys to detect, in order. Matched case-insensitively. | | timeout | number | 1000 | Ms of inactivity before the in-progress sequence resets. | | target | EventTarget | document | Element to listen on. Pass null for headless environments. | | preventDefault | boolean | true | Call event.preventDefault() on the final matching keypress. | | once | boolean | false | Fire at most once, then auto-disable. | | debug | boolean | false | Log internal state transitions to the console. | | maxAttempts | number | Infinity | Cap on successful activations. | | cooldown | number | 0 | Minimum ms between successful activations. | | autoEnable | boolean | false | Call enable() from the constructor. |

Methods

🔌 Lifecycle

| Method | Returns | Description | | ------------- | ----------------------- | -------------------------------------------------- | | enable() | Promise<KonamiCode> | Attach the keydown listener. | | disable() | Promise<KonamiCode> | Detach the listener and reset progress. | | reset() | void | Reset sequence progress without disabling. | | destroy() | Promise<void> | Disable, clear event listeners, null the callback. |

⚙️ Configuration

| Method | Returns | Description | | ------------------- | ------------ | -------------------------------------------- | | setCallback(fn) | KonamiCode | Replace the activation callback. | | setSequence(keys) | KonamiCode | Replace the key sequence and reset progress. |

🔍 State inspection

| Method | Returns | Description | | --------------- | ------------------------------------------ | ----------------------------------------- | | isEnabled() | boolean | Whether the listener is attached. | | isActivated() | boolean | Whether the code has fired at least once. | | getProgress() | { current, total, percentage } | Current position in the sequence. | | getStats() | { activations, attempts, lastActivated } | Copy of stats. | | resetStats() | KonamiCode | Zero out all stats. |

📣 Events

| Method | Returns | Description | | --------------------- | ----------------- | ------------------------------------------- | | on(event, handler) | KonamiCode | Register an event handler. | | off(event, handler) | KonamiCode | Remove an event handler. | | waitForActivation() | Promise<object> | Resolves on the next successful activation. |

🧪 Testing helpers

| Method | Returns | Description | | ------------------- | --------------- | -------------------------------------------------- | | triggerKey(key) | Promise<void> | Simulate a single keypress (no-op if not enabled). | | triggerSequence() | Promise<void> | Simulate the full configured sequence. |

Events

Fired via on() / off(). Handlers may be async; the emitter awaits them all.

| Event | Payload | When it fires | | ----------- | -------------------------------- | ----------------------------------------- | | enabled | { timestamp } | After enable() attaches the listener. | | disabled | { timestamp } | After disable() detaches it. | | progress | { current, total, percentage } | On every correct key in the sequence. | | activated | { timestamp, activations } | When the sequence completes. | | failed | { key, expected } | When a wrong key is pressed mid-sequence. | | error | { error, timestamp } | If the callback throws. |

A bubbling konamicode CustomEvent is also dispatched on the configured target when the code activates. Its detail is { timestamp, stats }.

Built-in sequences

import { SEQUENCES } from '@coffeeandfun/konami-code-detector';

SEQUENCES.classic; // ↑ ↑ ↓ ↓ ← → ← → B A
SEQUENCES.simple;  // ↑ ↓ ← →
SEQUENCES.debug;   // D E B U G
SEQUENCES.admin;   // A D M I N

All sequences are frozen. Pass your own array to sequence if none of them fit.


🧪 Testing

npm test              # one run
npm run test:watch    # watch mode
npm run test:coverage # coverage report

Tests are written with Jest and jsdom. They run under native ESM via --experimental-vm-modules, so jest is imported from @jest/globals inside test files rather than being a global.

📜 License

MIT — see LICENSE.