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

discord-lavalink

v1.1.1

Published

A TypeScript wrapper for Lavalink with Discord.js integration

Readme

discord-lavalink

ChatGPT Image May 2, 2025, 02_12_12 AM

A TypeScript wrapper for Lavalink v4 that works with Discord.js v14. Handles the WebSocket connection, REST calls, voice state forwarding, and auto-reconnect — so you can focus on building your bot instead of re-implementing all that every time.

Built this in about 2 hours from scratch. It covers what most music bots actually need.

What it does

  • Connects to Lavalink v4 over WebSocket and keeps the connection alive with exponential backoff reconnect
  • Searches across YouTube, YouTube Music, SoundCloud, Spotify, Deezer, Apple Music, and Yandex Music
  • Full playback control: play, stop, pause, seek, volume
  • Forwards voice state updates from Discord to Lavalink automatically
  • Session resuming via the Lavalink v4 sessions API
  • All audio filters Lavalink exposes: equalizer, karaoke, timescale, tremolo, vibrato, rotation, distortion, channelMix, lowPass
  • Full TypeScript types for everything

Installation

npm install discord-lavalink

Prerequisites

You need a running Lavalink v4 server. If you don't have one yet, see the setup guide.

You also need discord.js v14 and @discordjs/voice.

Quick start

import { Client, GatewayIntentBits } from 'discord.js';
import { joinVoiceChannel } from '@discordjs/voice';
import { Lavalink, LogLevel } from 'discord-lavalink';

const client = new Client({
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates],
});

const lavalink = new Lavalink({
  client,
  baseUrl: 'http://localhost:2333',
  password: 'youshallnotpass',
  logLevel: LogLevel.NORMAL,
});

lavalink.on('ready', (data) => {
  console.log(`Lavalink ready, session: ${data.sessionId}`);
});

lavalink.on('trackStart', (data) => {
  console.log(`Now playing: ${data.track.info.title}`);
});

client.on('ready', () => {
  lavalink.connect();
});

client.on('interactionCreate', async (interaction) => {
  if (!interaction.isCommand() || interaction.commandName !== 'play') return;

  const query = interaction.options.getString('song', true);
  const guildId = interaction.guildId;

  if (!guildId) {
    await interaction.reply('This command can only be used in a server!');
    return;
  }

  const member = interaction.member;
  if (!member || !('voice' in member) || !member.voice.channel) {
    await interaction.reply('You need to be in a voice channel!');
    return;
  }

  await interaction.deferReply();

  joinVoiceChannel({
    channelId: member.voice.channel.id,
    guildId,
    adapterCreator: interaction.guild.voiceAdapterCreator,
  });

  try {
    const result = await lavalink.tracks(query);

    if (result.loadType === 'empty' || result.loadType === 'error') {
      await interaction.editReply('Nothing found.');
      return;
    }

    await lavalink.play(guildId, result);
    const title =
      result.loadType === 'track'
        ? result.data.info.title
        : result.loadType === 'search'
          ? result.data[0].info.title
          : result.data.info.name;

    await interaction.editReply(`Now playing: ${title}`);
  } catch (error) {
    console.error(error);
    await interaction.editReply('Something went wrong.');
  }
});

client.login('YOUR_DISCORD_BOT_TOKEN');

Configuration

const lavalink = new Lavalink({
  client,
  baseUrl: 'http://localhost:2333',
  password: 'youshallnotpass',
  logLevel: LogLevel.NORMAL,
  customOptions: {
    maxReconnectAttempts: 5,
    reconnectInterval: 5000,
    headers: {},
    clientName: 'discord-lavalink/1.0.0',
  },
});

LogLevel.NORMAL only logs errors. LogLevel.DEBUG logs everything — useful when something isn't working and you need to see what's happening.

API

Connection

lavalink.connect();
lavalink.connect('previousSessionId');

lavalink.disconnect();

await lavalink.configureResuming(60);

Pass the previous session ID to connect() to resume a session. Call configureResuming() after connecting to tell Lavalink to keep session state alive for the given number of seconds.

Track loading

const result = await lavalink.tracks('never gonna give you up', 'ytsearch');
const result = await lavalink.loadTracks('https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M');

tracks() prepends the search type prefix automatically. If you pass a URL, it loads directly without a prefix. loadTracks() always loads from an identifier or URL as-is.

Playback

await lavalink.play(guildId, track);
await lavalink.play(guildId, track, {
  volume: 80,
  paused: false,
  position: 30000,
  noReplace: true,
  filters: {
    volume: 1.0,
    equalizer: [{ band: 0, gain: 0.2 }],
  },
});

await lavalink.stop(guildId);

await lavalink.pause(guildId, true);
await lavalink.pause(guildId, false);

await lavalink.seek(guildId, 60000);

await lavalink.setVolume(guildId, 50);

const player = await lavalink.getPlayer(guildId);

play() accepts a Datum object, a TracksResult (it picks the right track from it), or a raw encoded/identifier string. When noReplace is true, Lavalink won't replace a currently playing track.

Events

lavalink.on('ready', (data) => { });
lavalink.on('trackStart', (data) => { });
lavalink.on('trackEnd', (data) => { });
lavalink.on('trackException', (data) => { });
lavalink.on('trackStuck', (data) => { });
lavalink.on('playerUpdate', (data) => { });
lavalink.on('webSocketClosed', (data) => { });
lavalink.on('stats', (data) => { });

lavalink.off('trackStart', myHandler);

Search types

| Type | Source | |------|--------| | ytsearch | YouTube | | ytmsearch | YouTube Music | | scsearch | SoundCloud | | spsearch | Spotify (requires LavaSrc plugin) | | dzsearch | Deezer (requires LavaSrc plugin) | | amsearch | Apple Music (requires LavaSrc plugin) | | ymsearch | Yandex Music (requires LavaSrc plugin) |

Audio filters

Pass any of these in options.filters when calling play(), or update them with a separate play() call:

const filters = {
  volume: 1.0,
  equalizer: [
    { band: 0, gain: 0.2 },
    { band: 1, gain: 0.3 },
  ],
  karaoke: {
    level: 1.0,
    monoLevel: 1.0,
    filterBand: 220.0,
    filterWidth: 100.0,
  },
  timescale: {
    speed: 1.0,
    pitch: 1.0,
    rate: 1.0,
  },
  tremolo: { frequency: 2.0, depth: 0.5 },
  vibrato: { frequency: 2.0, depth: 0.5 },
  rotation: { rotationHz: 0.0 },
  distortion: {
    sinOffset: 0.0,
    sinScale: 1.0,
    cosOffset: 0.0,
    cosScale: 1.0,
    tanOffset: 0.0,
    tanScale: 1.0,
    offset: 0.0,
    scale: 1.0,
  },
  channelMix: {
    leftToLeft: 1.0,
    leftToRight: 0.0,
    rightToLeft: 0.0,
    rightToRight: 1.0,
  },
  lowPass: { smoothing: 20.0 },
};

See docs/audio-filters.md for what each filter actually does.

Lavalink server setup

See docs/lavalink-setup.md.

Examples

The example/ directory has a basic bot and a search example.

Contributing

Open a PR. Fork the repo, make your change, and submit it against main. If you're fixing a bug, a test case reproducing the issue is appreciated.

License

MIT — see LICENSE.

Credits

Built by TejasLamba2006.