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

lyrics-typing-engine

v2.0.0

Published

A typing engine designed for music-based typing games. Provides time-tagged typing map generation and kana/roma input evaluation synchronized with music playback.

Readme

lyrics-typing-engine

音楽に同期したタイピングゲーム用のエンジンライブラリ。歌詞データからタイピングマップを生成し、ローマ字入力・かな入力の判定を行います。

インストール

npm install lyrics-typing-engine

機能

タイピング譜面データの構築

buildTypingMap()

rawMapLines - ビルド前のタイピング譜面データ

charPoint - ローマ字換算での1打鍵あたりのポイント

import { buildTypingMap, type RawMapLine } from 'lyrics-typing-engine';

/**
 * @note 重要: タイピング譜面データはtime:0から始まり、最後の歌詞は"end"にする必要があります。
 */
const rawMapLines: RawMapLine[] = [
  { time: 0, lyrics: "こんにちは", word: "こんにちは" },
  { time: 3.5, lyrics: "世界", word: "せかい" },
  { time: 6.0, lyrics: "end", word: "" }
];

const builtMapLines = buildTypingMap({ rawMapLines, charPoint: 50 });

console.log(builtMapLines);
/**
 * [
 *   {
 *     time: 0,
 *     duration: 3.5,
 *     lyrics: "こんにちは",
 *     kanaLyrics: "こんにちは",
 *     romaLyrics: "konnitiha",
 *     wordChunks: [
 *       { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
 *       { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
 *       { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
 *       { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
 *       { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" }
 *     ],
 *     kpm: { kana: 85, roma: 154 },
 *     notes: { kana: 5, roma: 10 },
 *   },
 *   {
 *     time: 3.5,
 *     duration: 2.5,
 *     lyrics: "世界",
 *     kanaLyrics: "せかい",
 *     romaLyrics: "sekai",
 *     wordChunks: [
 *       { kana: "せ", romaPatterns: ["se", "ce"], point: 100, type: "kana" },
 *       { kana: "か", romaPatterns: ["ka", "ca"], point: 100, type: "kana" },
 *       { kana: "い", romaPatterns: ["i", "yi"], point: 50, type: "kana" }
 *     ],
 *     kpm: { kana: 72, roma: 120 },
 *     notes: { kana: 3, roma: 5 },
 *   },
 *   {
 *     time: 6,
 *     duration: 0,
 *     lyrics: "end",
 *     kanaLyrics: "",
 *     romaLyrics: "",
 *     wordChunks: [],
 *     kpm: { kana: 0, roma: 0 },
 *     notes: { kana: 0, roma: 0 }
 *   }
 * ]
 */

歌詞フレーズ切り替わり時のタイピングワード更新

createTypingWord(builtMapLine: BuiltMapLine, correct?: { kana: string; roma: string }) - builtMapLinesの行から次のフレーズのタイピングワードを作成

correct - 正解したローマ字・かな(デフォルトは空文字列)

import { createTypingWord } from 'lyrics-typing-engine';

const count = 0;

const timer = () => {
  const currentTime = video.getCurrentTime();
  const nextLine = builtMapLines[count + 1];

  if (currentTime >= nextLine.time) {
    count++;
    const newTypingWord = createTypingWord(nextLine);
    console.log(newTypingWord);
  }

}

/**
 * {
 *   correct: { kana: "", roma: "" },
 *   nextChunk: { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
 *   wordChunks: [
 *     { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
 *     { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
 *     { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
 *     { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
 *     { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" }
 *   ],
 *   wordChunksIndex: 1,
 * }
 */

入力の判定

isTypingKey(event: KeyboardEvent) - イベント時の文字入力キー判定

evaluateRomaInput({ event, typingWord, isCaseSensitive? }) - ローマ字入力時の判定

evaluateKanaInput({ event, typingWord, isCaseSensitive? }) - かな入力時の判定

isCaseSensitive - 大文字小文字を区別するかどうか(デフォルトはfalse)

import { isTypingKey, evaluateRomaInput, evaluateKanaInput } from 'lyrics-typing-engine';

const inputMode = "roma";

document.addEventListener('keydown', (event) => {
  if (!isTypingKey(event)) return;
  const typingWord = readTypingWord();

  const typingResult =
    inputMode === "roma"
      ? evaluateRomaInput({ event, typingWord })
      : evaluateKanaInput({ event, typingWord });

  if (typingResult.successKey) {
    // 正解時の処理
    console.log(typingResult);
  } else if (typingResult.failKey) {
    // ミス時の処理
  }
});

/**
 * {
 *   nextTypingWord: {
 *     correct: { kana: "こ", roma: "co" },
 *     nextChunk: { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
 *     wordChunks: [
 *       { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
 *       { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
 *       { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
 *       { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
 *       { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" }
 *     ],
 *     wordChunksIndex: 2
 *   },
 *   successKey: "o",
 *   failKey: undefined,
 *   chunkType: "kana",
 *   isCompleted: false,
 *   updatePoint: 100
 * }
 */

リプレイ再生用の入力判定関数

executeTypingInput({ inputChar, inputMode, typingWord, isCaseSensitive? })

isCaseSensitive - 大文字小文字を区別するかどうか(デフォルトはfalse)

import { executeTypingInput } from 'lyrics-typing-engine';

const replayData = [
  startInputMode: "roma",
  typeResults: [
    { time: 0, char: "c", isSuccess: true }
    { time: 1, char: "o", isSuccess: true }
    { time: 2, char: "a", isSuccess: false }
  ]
];

const typeResults = replayData[0].typeResults;
const inputMode = replayData[0].startInputMode;
const typingResult = executeTypingInput({
  inputChar: typeResults[0].char,
  inputMode,
  typingWord
});
console.log(typingResult);

/**
 * {
 *   nextTypingWord: {
 *     correct: { kana: "", roma: "c" },
 *     nextChunk: { kana: "こ", romaPatterns: ["o"], point: 100, type: "kana" },
 *     wordChunks: [
 *       { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
 *       { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
 *       { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
 *       { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
 *       { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" },
 *     ],
 *     wordChunksIndex: 1
 *   },
 *   successKey: "c",
 *   failKey: undefined,
 *   chunkType: "kana",
 *   isCompleted: false,
 *   updatePoint: 0
 * }
 */

表示用タイピングワードの生成

createDisplayState(typingWord: TypingWord, options?: { remainWord: { maxLength: number } }) - タイピングワードから表示用の状態を生成

typingWord - 現在のタイピングワード options.remainWord.maxLength - 未入力部分の表示文字数上限

入力済みのスペースは "ˍ" (U+02CD) に置換されます。 未入力半角スペースは " " (U+2004) に置換されます。

import { createDisplayState } from 'lyrics-typing-engine';

const displayState = createDisplayState(typingWord, { remainWord: { maxLength: 20 } });
console.log(displayState);
/**
 * {
 *   correct: { kana: "こ", roma: "co" },
 *   nextChar: { kana: "ん", roma: "nn" },
 *   remainWord: { kana: "にちは", roma: "nitiha" }
 * }
 *
 * // nextChar.roma は nextChunk.romaPatterns の最初のパターンが表示されます
 */

タイピングワード再生成

かな入力 → ローマ字入力 の動的なモード切り替え時に使用します。

recreateTypingWord(typingWord: TypingWord) - タイピングワードを再生成します。

import { recreateTypingWord } from 'lyrics-typing-engine';

const newTypingWord = recreateTypingWord(currentTypingWord);

カスタムオプション

ジェネリック型で独自のオプションを定義できます。

interface MyOptions {
  changeCSS?: string;
  changeVideoSpeed?: number;
}

const rawMapLines: RawMapLine<MyOptions>[] = [
  {
    time: 0,
    lyrics: "歌詞",
    word: "かし",
    options: { changeCSS: "color: red;" }
  }
];

const builtMapLines = buildTypingMap<MyOptions>({ rawMapLines, charPoint: 0 });

import 可能な型

// ビルド前タイピング譜面データ型
interface RawMapLine<TOptions = unknown> {
  time: string | number; // 時間(ミリ秒)
  lyrics: string; // 歌詞
  word: string; // ひらがなで記述されたタイピングワード
  options?: TOptions; // オプション(カスタムオプション)
}

// ビルド済みタイピング譜面データ型
interface BuiltMapLine<TOptions = unknown> {
  time: number; // 時間(ミリ秒)
  duration: number; // 行の時間の長さ(秒) - 次の行のtimeとの差分
  wordChunks: WordChunk[]; // ビルド済みタイピングワード
  lyrics: string; // 歌詞
  kpm: { kana: number; roma: number }; // フレーズの要求速度
  notes: { kana: number; roma: number }; // フレーズの要求打鍵数
  kanaLyrics: string; // かな表記の歌詞
  romaLyrics: string; // ローマ字表記の歌詞(各chunkの最初のパターン)
  options?: TOptions; // オプション(カスタムオプション)
}

// ビルド済みタイピングチャンク 型
interface WordChunk {
  kana: string; // ひらがな
  romaPatterns: string[]; // ローマ字パターン
  point: number; // ポイント
  type: "kana" | "alphabet" | "num" | "symbol" | "space" | undefined; // タイピングチャンクの種類
}

// タイピングモード 型
type InputMode = "roma" | "kana";

// タイピングワード 型
interface TypingWord {
  correct: { kana: string; roma: string }; // 正解したローマ字・かな
  nextChunk: WordChunk; // 次のタイピングチャンク
  wordChunks: WordChunk[]; // 全タイピングワード
  wordChunksIndex: number; // 現在のwordChunksの位置
  tempRomaPatterns?: string[]; // 一時的なローマ字パターン
}

// タイピング入力時の判定 型
interface TypingInputResult {
  nextTypingWord: TypingWord; // 更新後のタイピングワード (ミス時は実質更新されません)
  successKey: string | undefined; // 正解時の入力キー
  failKey: string | undefined; // ミス時の入力キー
  chunkType: WordChunk["type"]; // 入力したタイピングチャンクの種類
  isCompleted: boolean; // 打ち切り判定
  updatePoint: number; // 加算ポイント
}




ライセンス

MIT

作者

toshi7878