2

Violin Chart

PreviousNext

Violin plots showing probability density of data with kernel density estimation.

EngineeringSalesMarketingSupport20406080100120
"use client"

import { ViolinChart } from "@/components/ui/charts"

// Generate sample data with normal-ish distribution
const generateData = (mean: number, std: number, n: number, seed: number) => {
  const values: number[] = []
  let s = seed
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280
    const u1 = s / 233280
    s = (s * 9301 + 49297) % 233280
    const u2 = s / 233280
    const z = Math.sqrt(-2 * Math.log(u1 || 0.001)) * Math.cos(2 * Math.PI * u2)
    values.push(mean + std * z)
  }
  return values
}

const data = [
  { category: "Engineering", values: generateData(78, 12, 80, 1) },
  { category: "Sales", values: generateData(72, 15, 80, 2) },
  { category: "Marketing", values: generateData(75, 10, 80, 3) },
  { category: "Support", values: generateData(70, 18, 80, 4) },
]

export function ViolinChartDemo() {
  return <ViolinChart data={data} showBoxPlot showMedian />
}

About

Violin Charts combine box plots with kernel density estimation (KDE) to show the full distribution shape of data. The width of the violin at each value represents the probability density. Perfect for comparing distributions across multiple categories.

Installation

pnpm dlx shadcn@latest add https://ui.simplifying.ai/r/violin-chart.json

Usage

import { ViolinChart } from "@/components/ui/charts"
 
const data = [
  { category: "Group A", values: [12, 15, 18, 22, 25, 28, 30, 32, 35] },
  { category: "Group B", values: [8, 12, 15, 20, 25, 30, 35, 40, 45] },
]
 
export function Example() {
  return <ViolinChart data={data} showBoxPlot showMedian />
}

Examples

Default (Standard)

Standard violin chart with box plot overlay and median markers.

EngineeringSalesMarketingSupport20406080100120
"use client"

import { ViolinChart } from "@/components/ui/charts"

// Generate sample data with normal-ish distribution
const generateData = (mean: number, std: number, n: number, seed: number) => {
  const values: number[] = []
  let s = seed
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280
    const u1 = s / 233280
    s = (s * 9301 + 49297) % 233280
    const u2 = s / 233280
    const z = Math.sqrt(-2 * Math.log(u1 || 0.001)) * Math.cos(2 * Math.PI * u2)
    values.push(mean + std * z)
  }
  return values
}

const data = [
  { category: "Engineering", values: generateData(78, 12, 80, 1) },
  { category: "Sales", values: generateData(72, 15, 80, 2) },
  { category: "Marketing", values: generateData(75, 10, 80, 3) },
  { category: "Support", values: generateData(70, 18, 80, 4) },
]

export function ViolinChartDemo() {
  return <ViolinChart data={data} showBoxPlot showMedian />
}

Comparison

Before and after treatment comparison.

BeforeAfter020406080100
"use client"

import { ViolinChart } from "@/components/ui/charts"

// Generate sample data
const generateData = (mean: number, std: number, n: number, seed: number) => {
  const values: number[] = []
  let s = seed
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280
    const u1 = s / 233280
    s = (s * 9301 + 49297) % 233280
    const u2 = s / 233280
    const z = Math.sqrt(-2 * Math.log(u1 || 0.001)) * Math.cos(2 * Math.PI * u2)
    values.push(mean + std * z)
  }
  return values
}

// Simple before/after comparison
const data = [
  { category: "Before", values: generateData(45, 15, 100, 10) },
  { category: "After", values: generateData(68, 12, 100, 20) },
]

export function ViolinChartComparisonDemo() {
  return <ViolinChart data={data} showBoxPlot showMedian color="#22c55e" />
}

Box Plot Only

Shows only the box plot without the violin shape.

Q1Q2Q3Q4405060708090100110120
"use client"

import { ViolinChart } from "@/components/ui/charts"

// Generate sample data
const generateData = (mean: number, std: number, n: number, seed: number) => {
  const values: number[] = []
  let s = seed
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280
    const u1 = s / 233280
    s = (s * 9301 + 49297) % 233280
    const u2 = s / 233280
    const z = Math.sqrt(-2 * Math.log(u1 || 0.001)) * Math.cos(2 * Math.PI * u2)
    values.push(mean + std * z)
  }
  return values
}

// Box plot only variant - no violin shape
const data = [
  { category: "Q1", values: generateData(82, 8, 50, 1) },
  { category: "Q2", values: generateData(78, 12, 50, 2) },
  { category: "Q3", values: generateData(85, 10, 50, 3) },
  { category: "Q4", values: generateData(90, 6, 50, 4) },
]

export function ViolinChartBoxplotDemo() {
  return (
    <ViolinChart data={data} variant="boxplot" showMedian color="#8b5cf6" />
  )
}

Grouped

Test scores across multiple subjects.

MathScienceEnglishHistoryArt20406080100120
"use client"

import { ViolinChart } from "@/components/ui/charts"

// Generate sample data
const generateData = (mean: number, std: number, n: number, seed: number) => {
  const values: number[] = []
  let s = seed
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280
    const u1 = s / 233280
    s = (s * 9301 + 49297) % 233280
    const u2 = s / 233280
    const z = Math.sqrt(-2 * Math.log(u1 || 0.001)) * Math.cos(2 * Math.PI * u2)
    values.push(mean + std * z)
  }
  return values
}

// Test scores across multiple subjects
const data = [
  { category: "Math", values: generateData(72, 14, 80, 1) },
  { category: "Science", values: generateData(68, 16, 80, 2) },
  { category: "English", values: generateData(75, 12, 80, 3) },
  { category: "History", values: generateData(70, 15, 80, 4) },
  { category: "Art", values: generateData(80, 10, 80, 5) },
]

export function ViolinChartGroupedDemo() {
  return <ViolinChart data={data} showBoxPlot showMedian />
}

Custom Color

API response times with custom color.

GET /usersPOST /ordersGET /productsPUT /cart-50ms0ms50ms100ms150ms200ms250ms300ms
"use client"

import { ViolinChart } from "@/components/ui/charts"

// Generate sample data
const generateData = (mean: number, std: number, n: number, seed: number) => {
  const values: number[] = []
  let s = seed
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280
    const u1 = s / 233280
    s = (s * 9301 + 49297) % 233280
    const u2 = s / 233280
    const z = Math.sqrt(-2 * Math.log(u1 || 0.001)) * Math.cos(2 * Math.PI * u2)
    values.push(mean + std * z)
  }
  return values
}

// API response time distribution
const data = [
  { category: "GET /users", values: generateData(45, 20, 100, 1) },
  { category: "POST /orders", values: generateData(120, 40, 100, 2) },
  { category: "GET /products", values: generateData(35, 15, 100, 3) },
  { category: "PUT /cart", values: generateData(85, 25, 100, 4) },
]

export function ViolinChartHorizontalDemo() {
  return (
    <ViolinChart
      data={data}
      showBoxPlot
      showMedian
      color="#06b6d4"
      valueFormatter={(v) => `${v.toFixed(0)}ms`}
    />
  )
}

Minimal

Simple distribution without box plot overlay.

Group AGroup BGroup C01020304050607080
"use client"

import { ViolinChart } from "@/components/ui/charts"

// Generate sample data
const generateData = (mean: number, std: number, n: number, seed: number) => {
  const values: number[] = []
  let s = seed
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280
    const u1 = s / 233280
    s = (s * 9301 + 49297) % 233280
    const u2 = s / 233280
    const z = Math.sqrt(-2 * Math.log(u1 || 0.001)) * Math.cos(2 * Math.PI * u2)
    values.push(mean + std * z)
  }
  return values
}

// Simple age distribution - minimal style without box plot
const data = [
  { category: "Group A", values: generateData(35, 10, 60, 1) },
  { category: "Group B", values: generateData(42, 12, 60, 2) },
  { category: "Group C", values: generateData(38, 8, 60, 3) },
]

export function ViolinChartMinimalDemo() {
  return <ViolinChart data={data} showBoxPlot={false} showMedian />
}

API Reference

PropTypeDefaultDescription
dataViolinDataPoint[]requiredViolin data
variant"standard" | "boxplot" | "minimal""standard"Visual style
showBoxPlotbooleantrueShow box plot overlay
showMedianbooleantrueShow median marker
showGridbooleantrueShow grid lines
bandwidthnumberautoKDE bandwidth (auto-calculated)
resolutionnumber50KDE resolution (smoothness)
colorstring"#3b82f6"Color for all violins
valueFormatterfunctiontoFixed(0)Format display values

Data Format

interface ViolinDataPoint {
  category: string // Category label
  values: number[] // Array of numeric values
}

Variants

Standard

Full mirrored violin with optional box plot and median. Best for showing complete distribution shapes.

Box Plot

Shows only the box plot without the violin density shape. Classic statistical visualization.

Minimal

Simplified view focusing on the violin shape without overlays.

Features

  • Kernel Density Estimation: Automatic bandwidth calculation using Silverman's rule
  • Box plot overlay: Shows quartiles (Q1, median, Q3) and whiskers (min/max)
  • Interactive tooltips: Hover to see detailed statistics
  • Single consistent color: Clean, professional appearance
  • Dark mode support: Proper styling for both themes