# Getting Started

    library(ddplot)

`D3.js` is a famous JavaScript library that allows one to create extremely flexible SVG graphics however `D3` has (at least according to me) a pretty steep learning curve. Further, in order to understand some core concepts, one need to have some basics in `HTML`, `CSS` and `JavaScript`. `ddplot` aims to simply the process using a set of functions that render several graphics using a simple `R` API. Finally, `ddplot` is built upon the amazing `r2d3` package which makes it a breeze to interface `D3.js` with `R`, so a big thanks to the developers.

# `scatter_plot()`

Let’s work with the `mpg` data frame from the `ggplot2` package.

    library(ggplot2) # needed for the mpg data frame

    scatter_plot(
      data = mpg,
      x = "hwy",
      y = "cty",
      xtitle = "hwy variable",
      ytitle = "cty variable",
      title = "cty and hwy relationship",
      titleFontSize = 20
    )

In comparison to `ggplot2`, graphics’ customization in `ddplot` is limited nonetheless you get a fully vectorized SVG which is cool.

    scatter_plot(
      data = mpg,
      x = "displ",
      y = "cty",
      col = "tomato",
      bgcol = "pink",
      size = 3,
      stroke = "royalblue",
      strokeWidth = 1,
      xtitle = "displ variable",
      ytitle = "cty variable",
      xticks = 3,
      yticks = 3)

# `histogram()`

The `histogram()` function allows you to visualize the distribution of a vector of data:

    histogram(
      x = mpg$hwy,
      bins = 20,
      fill = "crimson",
      stroke = "white",
      strokeWidth = 1,
      title = "Distribution of the hwy variable",
      width = "20",
      height = "10"
    )

# `animated_histogram()`

This function allows you to create a one-click histogram animation. Useful for presentation purposes. Click on the following empty plot and see what happens:

    animated_histogram(
      x = mpg$hwy,
      duration = 2000,
      delay = 100,
      fill = "lime",
      stroke = "white",
      bgcol = "white"
      )

Note that you can customize the animation using the two parameters `duration` and `delay`.

# `flower()`

The `flower()` function allows you to plot a simple flowers with a defined number of petals. You can also define the width/length of the petals and rotate them if needed:

    flower(
      petalCount = 7,
      petalColor = "plum",
      rotationSpeed = 0
    )

    flower(
      petalCount = 15,
      petalWidth = 20,
      petalColor = "plum",
      rotationSpeed = 1.5
    )

# `heart_fill`

The `heart_fill` function allows you to display a heart indicator which will be filled according to a numerical value from 0 to 1:

    heart_fill(fill_level = 0.8, titleText = NULL, renderFillLabel = FALSE)

# `glass_fill`

The `glass_fill` function works the same way as the `heart_fill` above:

    glass_fill(fill_level = 0.8, titleText = "Glass of water", renderFillLabel = TRUE)

# `plant_growth`

The `plant_growth` function can also be used as a visual clue:

    plant_growth(0.4)

    plant_growth(1)

# `pulse_grid()`

The `pulse_grid()` function is useful when you have a compact matrix of values and you want the viewer to immediately spot where activity, load, risk, or pressure is concentrated. It works especially well for operational monitoring and staffing views because the pulsing highlights the hottest cells without requiring interaction.

## Example 1: Team workload by hour

This is a good fit for support teams, call centers, or editorial desks. Instead of reading a table of numbers, you can see which teams are under the heaviest load at which times of day.

    workload_grid <- data.frame(
      hour = c("09:00","12:00","15:00","18:00",
               "09:00","12:00","15:00","18:00",
               "09:00","12:00","15:00","18:00"),
      team = c("North","North","North","North",
               "South","South","South","South",
               "East","East","East","East"),
      load = c(0.35,0.72,0.56,0.91,
               0.41,0.67,0.83,0.58,
               0.28,0.49,0.77,0.63),
      stringsAsFactors = FALSE
    )

    pulse_grid(
      data = workload_grid,
      x = "hour",
      y = "team",
      value = "load",
      title = "Support Team Load Through the Day",
      low = "#D9F0FF",
      high = "red",
      bgcol = "#0B132B",
      stroke = "#1C2541",
      strokeWidth = 1.5,
      pulseStrength = 0.22
    )

Why this is useful: it gives managers a compact way to decide when to reallocate staff, stagger breaks, or add temporary coverage. The pulse effect makes the high-pressure windows stand out much faster than a plain heatmap.

## Example 2: Reliability monitoring by service and weekday

This second example works well for engineering or product teams. A pulse grid can summarize incident rate, latency, or error volume across services and days of the week, making it easier to see repeated pressure points in a weekly operating rhythm.

    reliability_grid <- data.frame(
      day = c(
        "Mon","Tue","Wed","Thu","Fri",
        "Mon","Tue","Wed","Thu","Fri",
        "Mon","Tue","Wed","Thu","Fri",
        "Mon","Tue","Wed","Thu","Fri"
      ),
      service = c(
        "API","API","API","API","API",
        "Billing","Billing","Billing","Billing","Billing",
        "Search","Search","Search","Search","Search",
        "Auth","Auth","Auth","Auth","Auth"
      ),
      incident_score = c(
        0.62, 0.44, 0.57, 0.78, 0.69,
        0.31, 0.36, 0.40, 0.51, 0.47,
        0.73, 0.81, 0.76, 0.88, 0.67,
        0.22, 0.27, 0.25, 0.34, 0.29
      ),
      stringsAsFactors = FALSE
    )

    pulse_grid(
      data = reliability_grid,
      x = "day",
      y = "service",
      value = "incident_score",
      title = "Service Reliability Pressure Map",
      low = "lightgreen",
      high = "#B00020",
      bgcol = "#081C15",
      labelColor = "#F1FAEE",
      titleColor = "#F1FAEE",
      stroke = "#2D6A4F",
      showValues = TRUE,
      digits = 2
    )

Why this is useful: repeated hot cells reveal where engineering effort should go first. In this example, a team can immediately see whether one service is consistently unstable or whether problems cluster around a particular day in the deployment cycle.

# `flame`

You can create cool `flames` using the `flame` function, useful if you want to display the state of a critical information that yields a numerical values (for example, the number of errors encountered in a specific process):

    flame(
      intensity = 20,
      flameGradientColors = c("yellow", "orange", "darkred"),
      flameOutline = "darkred",
      bgcol = "#fefefe"
    )

    flame(
      intensity = 80,
      flameGradientColors = c("green", "darkgreen", "darkblue"),
      flameOutline = "#fefefe",
      bgcol = "black"
    )

# `parliament_chart()`

You can easily create a `Parliamant chart` using the `parliamant_chart()` function, consider the following example:

    # The data is a toy example and does not reflect the reality
    vote_results_germany <- data.frame(
        political_party = c("SDP", "CDU", "Linke", "Grüne"),
        number_of_seats = c(200, 40, 30, 20)
    )
    parliament_chart(
      data = vote_results_germany,
      categorical_column = "political_party",
      numerical_column = "number_of_seats",
      title = "German Bundestag",
      seatSize = 10,
      bgcol = "#fefefe"
    )

# `bar_chart()`

The `barChat()` function allows you to create bar charts however you need to make the aggregation beforehand. In the following example, we will plot the average `cty` for each `manufacturer` using the `dplyr` package.

    library(dplyr)

    mpg %>% group_by(manufacturer) %>%
      summarise(mean_cty = mean(cty)) %>%
      bar_chart(
        x = "manufacturer",
        y = "mean_cty",
        xFontSize = 10,
        yFontSize = 10,
        fill = "orange",
        strokeWidth = 2,
        ytitle = "average cty value",
        title = "Average City Miles per Gallon by manufacturer"
      )

The bars can be easily sorted in `ascending` or `descending` order using the `sort` parameter:

    mpg %>% group_by(manufacturer) %>%
      summarise(mean_cty = mean(cty)) %>%
      bar_chart(
        x = "manufacturer",
        y = "mean_cty",
        sort = "ascending",
        xFontSize = 10,
        yFontSize = 10,
        fill = "orange",
        strokeWidth = 1,
        ytitle = "average cty value",
        title = "Average City Miles per Gallon by manufacturer",
        titleFontSize = 16
      )

# `horz_bar_chart()`

If you’ve many categories, it might be a good idea to go for a horizontal bar chart. It has the same parameters as the `bar_chart()` function except that the x-axis parameter is named `value` and the y-axis parameter named `label`, this naming convention aims to mitigate some confusion that can arise.

If we want to replicate the above graphic in a horizontal way, we can do:

    mpg %>% group_by(manufacturer) %>%
      summarise(mean_cty = mean(cty)) %>%
      horz_bar_chart(
        label = "manufacturer",
        value = "mean_cty",
        sort = "ascending",
        labelFontSize  = 10,
        valueFontSize = 10,
        fill = "orange",
        stroke = "crimson",
        strokeWidth = 1,
        valueTitle  = "average cty value",
        title = "Average City Miles per Gallon by manufacturer",
        titleFontSize = 16
      )

As in `bar_chart()`, we can aslo sort in descending order:

    mpg %>% group_by(manufacturer) %>%
      summarise(mean_cty = mean(cty)) %>%
      horz_bar_chart(
        label = "manufacturer",
        value = "mean_cty",
        sort = "descending",
        labelFontSize  = 10,
        valueFontSize = 10,
        bgcol = "black",
        axisCol = "white",
        fill = "white",
        stroke = "white",
        strokeWidth = 1,
        valueTitle  = "average cty value",
        labelTitle = "Manufacturers",
        title = "Average City Miles per Gallon by manufacturer",
        titleFontSize = 16
      )

# `lollipop_chart()`

lollipop chart follows the same behavior as bar charts but instead of bars you get lollipops, hence the name. Below an example of a lollipop chart with `ddplot`:

    mpg %>% group_by(drv) %>%
      summarise(median_cty = median(cty)) %>%
      lollipop_chart(
        x = "drv",
        y = "median_cty",
        sort = "ascending",
        xtitle = "drv variable",
        ytitle = "median cty",
        title = "Median cty per drv",
        xFontSize = 20
      )

It’s possible to grasp the distribution of some variable according to a specific categorical variable using the same function:

    mpg %>% filter(year == 2008) %>%
    lollipop_chart(
        x = "manufacturer",
        y = "hwy",
        circleFill = 'red',
        circleStroke = 'orange',
        circleRadius = 5,
        sort = "none",
        xFontSize = 10
      )

From above, it’s quite easy to notice that although Toyota has two cars with high highway miles per galon (hwy), it also produces many other vehicles with poor hwy.

# `horz_lollipop()`

Same with bar charts, if you have a variable that has many categorical values, you can work with the reversed version of `lollipop_chart()` which is `horz_lollipop()`:

    mpg %>% group_by(manufacturer) %>%
      summarise(median_cty = median(cty)) %>%
      horz_lollipop(
        label = "manufacturer",
        value = "median_cty",
        sort = "descending")

You can also do:

    mpg %>% filter(year == 2008) %>%
    horz_lollipop(
        label = "manufacturer",
        value = "hwy",
        circleFill = 'red',
        circleStroke = 'orange',
        circleRadius = 5,
        sort = "none"
      )

# `pie_chart()`

Pie charts and donut charts are pretty straightforward to set up. We’ll use a sample from the `starwars` data frame to plot a simple pie chart.

    # starwars is part of the dplyr data frame
    mini_starwars <- starwars %>% tidyr::drop_na(mass) %>%
      sample_n(size = 5) # getting 5 random values

    pie_chart(
      data = mini_starwars,
      value = "mass",
      label = "name"
    )

Using the `padRadius`, `padAngle` and `cornerRadius` parameters, one can get fanciers pie charts:

    pie_chart(
      data = mini_starwars,
      value = "mass",
      label = "name",
      padRadius = 200,
      padAngle = 0.1,
      cornerRadius = 50,
      innerRadius = 10
    )

If you need a donut chart, you just need to play with the `innerRadius` parameter:

    pie_chart(
      data = mini_starwars,
      value = "mass",
      label = "name",
      innerRadius = 120,
      cornerRadius = 20,
      title = "5 Starwars characters ranked by their mass",
      titleFontSize = 16,
      bgcol = "yellow"
    )

# `line_chart()`

The `line_chart()` function is used to plot time series data. The use must provide a `date` variable that has the `yyyy-mm-dd` format. In the following example, we’ll use the `Air Passenger` built-in `ts` data and convert it to a classical data frame:

    # 1. converting AirPassengers to a tidy data frame
    airpassengers <- data.frame(
      passengers = as.matrix(AirPassengers),
      date= zoo::as.Date(time(AirPassengers))
    )

    # 2. plotting the line chart
    line_chart(
      data = airpassengers,
      x = "date",
      y = "passengers"
    )

You can modify the line interpolation using the `curve` parameter:

    line_chart(
      data = airpassengers,
      x = "date",
      y = "passengers",
      curve = "curveStep"
    )

    line_chart(
      data = airpassengers,
      x = "date",
      y = "passengers",
      curve = "curveCardinal"
    )

    line_chart(
      data = airpassengers,
      x = "date",
      y = "passengers",
      curve = "curveBasis"
    )

# `anim_line_chart()`

Heavily inspired from Jure Stabuc’s example, the `anim_line_chart()` function create an empty SVG but when each time you click on it a line chart animation starts. Note that the line lasts after the end of the animation. Go ahead, click on the empty graphic below:

    anim_line_chart(
      data = airpassengers,
      x = "date",
      y = "passengers",
      duration = 10000, # in milliseconds (10 seconds)
      curve = "curveCardinal"
      )

# `area_chart()`

`area_chart()` works similarly except that instead of a line you get an area.

    # 1. converting AirPassengers to a tidy data frame
    airpassengers <- data.frame(
      passengers = as.matrix(AirPassengers),
      date= zoo::as.Date(time(AirPassengers))
    )

    # 2. plotting the area chart
    area_chart(
      data = airpassengers,
      x = "date",
      y = "passengers",
      fill = "purple",
      bgcol = "white"
    )

# `area_band()`

`area_band()` lets you plot a filled area between two y-values. For the sake of the example, let’s create an additional column `passengers_upper` that has an additional 40 passengers for each observation:

    airpassengers <- data.frame(
      passengers_lower = as.matrix(AirPassengers),
      passengers_upper = as.matrix(AirPassengers) + 40,
      date= zoo::as.Date(time(AirPassengers))
    )

    area_band(
      data = airpassengers,
      x = "date",
      yLower = "passengers_lower",
      yUpper = "passengers_upper",
      fill = "yellow",
      stroke = "black"
    )

# `stacked_area_chart()`

This function allows you to create a stacked area chart. You need two components:

- A data frame in wide format (see an example below). If it’s in wide format, you can still use `pivot_wider()` from the `tidyr` package to make wider.
- A date variable in `yyyy-mm-dd` format that will plotted in the x-axis.

Let’s work with the following data frame (shortened) provided by Mike Bostock in his stacked area chart example:

    data <- data.frame(
      date = c(
        "2000-01-01", "2000-02-01", "2000-03-01", "2000-04-01",
        "2000-05-01", "2000-06-01", "2000-07-01",
        "2000-08-01", "2000-09-01", "2000-10-01"
      ),
      Trade = c(
        2000,1023, 983, 2793, 1821, 1837, 1792, 1853, 791, 739
      ),
      Manufacturing = c(
        734, 694, 739, 736, 685, 621, 708, 685, 667, 693
      ),
      Leisure = c(
        1782, 1779, 1789, 658, 675, 833, 786, 675, 636, 691
      ),
      Agriculture = c(
        655, 587,623, 517, 561, 2545, 636, 584, 559, 2504
      )
    )

    data
    #>          date Trade Manufacturing Leisure Agriculture
    #> 1  2000-01-01  2000           734    1782         655
    #> 2  2000-02-01  1023           694    1779         587
    #> 3  2000-03-01   983           739    1789         623
    #> 4  2000-04-01  2793           736     658         517
    #> 5  2000-05-01  1821           685     675         561
    #> 6  2000-06-01  1837           621     833        2545
    #> 7  2000-07-01  1792           708     786         636
    #> 8  2000-08-01  1853           685     675         584
    #> 9  2000-09-01   791           667     636         559
    #> 10 2000-10-01   739           693     691        2504

Note that when running `stacked_area_chart()` all the variables available within the considered data frame will be plotted. If you want to restrict the plotting to only specific variables, just drop the unneeded columns:

    stacked_area_chart(
      data = data,
      x = "date",
      legendTextSize = 14
      )

You can modify the color scheme using the `colorCategory` parameter:

    stacked_area_chart(
      data = data,
      x = "date",
      legendTextSize = 14,
      curve = "curveCardinal",
      colorCategory = "Accent",
      bgcol = "white",
      stroke = "black",
      strokeWidth = 1
      )

    stacked_area_chart(
      data = data,
      x = "date",
      legendTextSize = 14,
      curve = "curveBasis",
      colorCategory = "Set3",
      bgcol = "black",
      axisCol = "white",
      xticks = 4,
      stroke = "black"
      )

You can find list of D3 categorical color schemes here

Finally, if you hover over the chart you’ll notice a tooltip that identified the different area categories.

# `bar_chart_race()`

This function allows you to create an animated bar chart race. `bar_chart_race()` is similar to `bar_chart()` but takes a third variable mapped to the time dimension, with options for styling transitions.

Let’s make a bar chart race of population growth among various countries using a subset of the `gapminder` dataset from the {gapminder} package:

    gapminder_subset <- gapminder::gapminder %>%
      select(country, year, pop) %>%
      filter(country %in% c("Japan", "Mexico", "Germany", "Brazil", "Philippines", "Vietnam")) %>%
      mutate(pop = pop/1e6)

    gapminder_subset %>%
      slice_sample(n = 10)

    #>    year       pop     country
    #> 1  2007  91.07729 Philippines
    #> 2  1997  76.04900     Vietnam
    #> 3  1972 107.18827       Japan
    #> 4  1967  39.46391     Vietnam
    #> 5  1952  30.14432      Mexico
    #> 6  1987 142.93808      Brazil
    #> 7  1997 168.54672      Brazil
    #> 8  1962  41.12148      Mexico
    #> 9  1952  69.14595     Germany
    #> 10 1957  91.56301       Japan

In this example, we simply pass call `bar_chart_race()` like `bar_chart()`, but with an additional variable mapped to the time dimension specified with `time = year`:

    gapminder_subset %>%
      bar_chart_race(
        x = "pop",
        y = "country",
        time = "year",
        ytitle = "Country",
        xtitle = "Population (in millions)",
        title = "Bar chart race of country populations"
      )

You can also stylize transitions with the `frameDur`, `transitionDur`, and `ease` arguments. For example, setting the time spent pausing on each frame to zero with `frameDur = 0` will create a smooth animation:

    gapminder_subset %>%
      bar_chart_race(
        x = "pop",
        y = "country",
        time = "year",
        transitionDur = 1000,
        frameDur = 0,
        ytitle = "Country",
        xtitle = "Population (in millions)",
        title = "Bar chart race of country populations"
      )

As you might have noticed, the value of the column passed to the `time` argument is automatically labelled at the bottom-right corner of the plot panel. We can stylize this with a list of options passed to the `timeLabelOpts` argument (or turn it off with `timeLabel = FALSE`). We also give the bars a little bounce here with `ease = "BackInOut"` for fun.

    gapminder_subset %>%
      bar_chart_race(
        x = "pop",
        y = "country",
        time = "year",
        ease = "BackInOut",
        ytitle = "Country",
        xtitle = "Population (in millions)",
        title = "Bar chart race of country populations",
        timeLabelOpts = list(
          size = 40,
          prefix = "Year: ",
          xOffset = 0.2
        )
      )

# `liquid_chart()`

`liquid_chart()` renders a single value as a circle filled with an animated liquid. The water rises from empty to the target level on load, two staggered wave layers give the surface a natural depth, and the label text switches color at the water line — appearing in `textColor` above the surface and in `waveTextColor` inside the water.

    liquid_chart(value = 0.55, title = "Memory")

Colors, wave behaviour, and the label are all configurable:

    liquid_chart(
      value         = 0.28,
      label         = "28%",
      fillColor     = "tomato",
      circleColor   = "tomato",
      textColor     = "tomato",
      waveTextColor = "white",
      title         = "Errors"
    )

    liquid_chart(
      value         = 0.72,
      label         = "72%",
      fillColor     = "#27ae60",
      waveAmplitude = 0.05,
      waveCount     = 3,
      waveSpeed     = 1.5,
      title         = "Battery"
    )

# `waterfall_chart()`

A waterfall chart shows how an initial value is built up or eroded by a sequence of positive and negative contributions. Each bar starts exactly where the previous one ended. It is the standard chart for P&L decomposition, budget variance analysis, and any “what changed and why” narrative.

The optional `measure` column lets you mark certain bars as `"total"`, they span from zero to the running cumulative sum, making subtotals and final totals visually distinct. Hovering over any bar shows a tooltip with the label, the delta, and the running total.

    pnl <- data.frame(
      label   = c("Revenue", "COGS", "Gross Profit",
                  "R&D", "S&M", "G&A", "Operating Income"),
      value   = c(1200, -450, 750, -120, -90, -60, 480),
      measure = c("relative", "relative", "total",
                  "relative", "relative", "relative", "total")
    )

    waterfall_chart(
      data   = pnl,
      x      = "label",
      y      = "value",
      measure = "measure",
      title  = "P&L bridge",
      ytitle = "USD thousands"
    )

Without a `measure` column every bar is a simple delta — useful for tracking a running balance over time:

    cashflow <- data.frame(
      month = c("Jan", "Feb", "Mar", "Apr", "May", "Jun"),
      delta = c(120, -30, 80, -60, 95, -20)
    )

    waterfall_chart(
      data          = cashflow,
      x             = "month",
      y             = "delta",
      title         = "Monthly cash flow",
      ytitle        = "USD thousands",
      positiveColor = "steelblue",
      negativeColor = "tomato"
    )

# `dumbbell_chart()`

A dumbbell chart (also called a connected dot plot) shows two values per category as dots joined by a horizontal line. It is the clearest way to communicate the gap or change between two groups, time points, or conditions across many categories , a grouped bar chart would require much more visual scanning to reach the same conclusion. The New York Times and The Economist use this format regularly.

    life_exp <- data.frame(
      country   = c("Brazil", "China", "Egypt", "India",
                    "Japan", "Mexico", "Nigeria", "Turkey"),
      year_1952 = c(50.9, 44.0, 41.9, 37.4, 63.0, 50.8, 36.3, 43.6),
      year_2007 = c(72.4, 72.9, 71.3, 64.7, 82.6, 76.2, 46.9, 71.8)
    )

    dumbbell_chart(
      data     = life_exp,
      x1       = "year_1952",
      x2       = "year_2007",
      y        = "country",
      x1Label  = "1952",
      x2Label  = "2007",
      title    = "Life expectancy: 1952 vs 2007",
      xtitle   = "Life expectancy (years)",
      sort     = "ascending"
    )

The chart works equally well for comparing two conditions within the same time period. Here we compare average city and highway fuel economy across vehicle classes using the `mpg` dataset:

    library(dplyr)

    mpg_summary <- mpg |>
      group_by(class) |>
      summarise(city = mean(cty), highway = mean(hwy))

    dumbbell_chart(
      data    = mpg_summary,
      x1      = "city",
      x2      = "highway",
      y       = "class",
      x1Label = "City",
      x2Label = "Highway",
      col1    = "steelblue",
      col2    = "darkorange",
      title   = "City vs highway fuel economy by class",
      xtitle  = "Miles per gallon",
      sort    = "ascending"
    )

# `beeswarm_plot()`

A beeswarm plot shows every individual data point along a numeric axis, spreading them sideways so they never overlap. It is far more transparent than a box plot because nothing is hidden behind a summary statistic — you see the full shape, outliers, and clusters of the raw data. The New York Times graphics desk uses this style regularly to remind readers that numbers represent real people.

The layout is computed with a D3 force simulation: each point is pulled toward its true value on the x-axis while a collision force prevents any two circles from touching.

    library(ggplot2) # for the mpg dataset

    # Single swarm — full distribution of highway fuel economy
    beeswarm_plot(
      data   = mpg,
      x      = "hwy",
      col    = "steelblue",
      xtitle = "Highway miles per gallon",
      title  = "Distribution of hwy"
    )

When a `group` column is provided, one swarm is drawn per group and colored automatically:

    beeswarm_plot(
      data   = mpg,
      x      = "hwy",
      group  = "class",
      xtitle = "Highway miles per gallon",
      title  = "Highway fuel economy by vehicle class",
      tooltip = "manufacturer"
    )

You can swap the dataset and adjust the look freely. Here using the built-in `iris` dataset with a different color palette and larger points:

    beeswarm_plot(
      data         = iris,
      x            = "Sepal.Length",
      group        = "Species",
      colorPalette = "Set2",
      radius       = 5,
      opacity      = 0.8,
      xtitle       = "Sepal length (cm)",
      title        = "Sepal length by species"
    )

# `bullet_chart()`

The `bullet_chart()` function renders a bullet chart — a compact alternative to gauge charts designed by Stephen Few. It displays one actual value (the dark bar) against shaded qualitative bands (e.g. poor / satisfactory / good) and an optional target marker (the short vertical line).

The `ranges` argument defines the upper bound of each band; the first band starts at `min` (default 0). Colors default to a sequence of grays from darker (lower range) to lighter (higher range).

    bullet_chart(
      value    = 270,
      target   = 300,
      ranges   = c(150, 225, 350),
      title    = "Revenue",
      subtitle = "USD thousands"
    )

You can stack several bullet charts side by side to compare multiple KPIs at a glance:

    bullet_chart(
      value      = 7.4,
      target     = 8.0,
      ranges     = c(4, 7, 10),
      title      = "Satisfaction",
      subtitle   = "out of 10",
      valueColor = "steelblue"
    )

When a lower value is better (e.g. response time), simply order your `rangeColors` from lightest (best, lowest range) to darkest (worst, highest range):

    bullet_chart(
      value       = 320,
      target      = 250,
      ranges      = c(200, 500, 1000),
      rangeColors = c("#f0f0f0", "#d9d9d9", "#bdbdbd"),
      title       = "Response",
      subtitle    = "ms"
    )

# `gauge_chart()`

The `gauge_chart()` function renders a speedometer-style gauge. It takes a single numeric `value` and displays it on a circular dial spanning from `min` to `max`. The arc is divided into three zones: normal (plain), warning, and danger, each with its own color.

    gauge_chart(
      value = 8,
      title = "Memory"
    )

You can adjust the thresholds and colors to match your context. Here the warning zone starts at 60% and the danger zone at 80%:

    gauge_chart(
      value = 72,
      min = 0,
      max = 100,
      title = "CPU Load",
      warningZone  = 60,
      warningColor = "orange",
      dangerZone   = 80,
      dangerColor  = "red"
    )

The gauge is not limited to percentages, any numeric range works. Below is an example monitoring server response time in milliseconds, where anything above 400 ms is a warning and above 700 ms is critical:

    gauge_chart(
      value = 530,
      min = 0,
      max = 1000,
      title = "Response (ms)",
      warningZone  = 400,
      warningColor = "orange",
      dangerZone   = 700,
      dangerColor  = "red"
    )

# Using `ddplot` in `Shiny`

If you want to add reactivity to your `ddplot` plots, use the `uiOutput` and `renderUI` in order to render a frame, note that you’ll need to have a `www` folder, below an example:

    library(shiny)
    library(ddplot)
    library(r2d3)

    ui <- fluidPage(

      shiny::h2("Example ddplot application"),

      shiny::selectInput(
        inputId = "colors",
        label = NULL,
        choices = colors(),
        selected = "springgreen3"
      ),

      shiny::br(),

      shiny::sliderInput(
        inputId = "slider",
        label = NULL,
        min = 1,
        max = 10,
        value = 3
      ),
      shiny::br(),
      mainPanel(
        uiOutput("ddplot_ui")
      )
    )

    server <- function(input, output) {
      output$ddplot_ui <- renderUI({
        widget <- ddplot::scatter_plot(
          data = iris,
          x = "Sepal.Length",
          y = "Sepal.Width",
          size = input$slider,
          col = input$colors
        )
        htmlwidgets::saveWidget(widget, "www/temp_ddplot.html", selfcontained = TRUE)
        tags$iframe(src = "temp_ddplot.html", width = "100%", height = "400px", frameborder = 0)
      })
    }

    shinyApp(ui = ui, server = server)