Color Alpha Support

Lets-Plot-Kotlin accepts color values that include an alpha channel. This makes it possible to keep the same base color while controlling transparency for annotations, labels, and other plot elements.

The alpha component can be embedded directly in the color string in the following formats:

Format Example Alpha range
name / a steelblue / 0.14 float 0.0 (transparent) - 1.0 (opaque)
rgba(r, g, b, a) rgba(70, 130, 180, 0.14) float 0.0 (transparent) - 1.0 (opaque)
#RRGGBBAA #4682B424 hex byte 00-FF
#RGBA #48B2 hex nibble 0-F (expanded to #4488BB22)
color(r, g, b, a) color(70, 130, 180, 0.14) float 0.0 - 1.0

For alpha, zero means a transparent color, and 1.0 (hex FF) means opaque. Some of these formats also work without the alpha component for fully opaque colors (green, rgb(...), #RRGGBB, #RGB).

In [1]:
%useLatestDescriptors
%use lets-plot
In [2]:
LetsPlot.getInfo()
Out[2]:
Lets-Plot Kotlin API v.4.14.0. Frontend: Notebook with dynamically loaded JS. Lets-Plot JS v.4.10.1.
Outputs: Web (HTML+JS), Kotlin Notebook (Swing), Static SVG (hidden)
In [3]:
import java.time.LocalDate
import java.time.ZoneOffset

fun epochMillis(year: Int, month: Int, day: Int): Long =
    LocalDate.of(year, month, day).atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli()

val months = (1..6).map { month -> epochMillis(2024, month, 1) }

val forecast = mapOf(
    "month" to months,
    "leads" to listOf(18, 29, 41, 57, 63, 74),
    "lead_label" to listOf("18k", "29k", "41k", "57k", "63k", "74k")
)

val seriesColor = "steelblue"
val areaFill = "mediumturquoise"
val paper = "aliceblue"
val mutedText = "slategray"
val titleText = "midnightblue"

val basePlot = letsPlot(forecast) { x = "month"; y = "leads" } +
    geomArea(fill = areaFill, alpha = 0.35) +
    geomLine(color = seriesColor, size = 2.2) +
    geomPoint(color = seriesColor, fill = "white", shape = 21, size = 4.5, stroke = 1.6) +
    geomText(nudgeY = 6, size = 10, color = seriesColor) { label = "lead_label" } +
    scaleXDateTime(breaks = months, format = "%b") +
    scaleYContinuous(limits = 0 to 86, breaks = listOf(0, 20, 40, 60, 80)) +
    ggsize(760, 420) +
    theme(
        plotBackground = elementRect(fill = paper, color = paper),
        panelBackground = elementRect(fill = paper, color = paper),
        panelGridMinor = elementBlank(),
        panelGridMajorX = elementBlank(),
        axisText = elementText(color = mutedText),
        axisTitleY = elementText(color = mutedText),
        plotTitle = elementText(color = titleText, face = "bold", size = 20),
        plotSubtitle = elementText(color = mutedText, size = 11)
    )

Plot Tag

Here, the plot tag receives a translucent color value directly from rgba(...).

In [4]:
basePlot +
    labs(
        title = "Partner pipeline forecast",
        subtitle = "The semi-transparent tag color is specified with an alpha channel",
        tag = "DRAFT",
        x = "",
        y = "Qualified leads"
    ) +
    theme(
        plotTag = elementText(
            color = "rgba(70, 130, 180, 0.14)",   // alpha is part of the color value
            face = "bold",
            size = 180,
            angle = 40
        ),
        plotTagLocation = "panel",
        plotTagPosition = listOf(0.5, 0.5)
    )
Out[4]:
18k 29k 41k 57k 63k 74k Jan Feb Mar Apr May Jun 0 20 40 60 80 DRAFT Partner pipeline forecast The semi-transparent tag color is specified with an alpha channel Qualified leads

Geom Text

The same plot can place the translucent annotation in an inclined geomText() layer instead of using the plot tag.

Note. Each annotation row carries its own color string, using one of different alpha-enabled formats that represent nearly the same color. The colors are mapped with scaleColorIdentity().

In [5]:
val alphaDf = mapOf(
    "x" to listOf(epochMillis(2024, 5, 5), epochMillis(2024, 3, 15), epochMillis(2024, 3, 18), epochMillis(2024, 1, 29)),
    "y" to listOf(62.5, 62.5, 28.5, 28.5),
    "label" to List(4) { "ALPHA" },
    "color" to listOf(
        "steelblue / 0.14",           // named color + alpha
        "rgba(70, 130, 180, 0.14)",   // rgba(...) with float alpha
        "#4682B424",                  // #RRGGBBAA hex
        "#48B2"                       // #RGBA short hex, almost the same appearance
    )
)

basePlot +
    geomText(
        data = alphaDf,
        inheritAes = false,
        size = 34,
        angle = -30,
        fontface = "bold"
    ) { x = "x"; y = "y"; label = "label"; color = "color" } +
    scaleColorIdentity() +
    labs(
        title = "Partner pipeline forecast",
        subtitle = "Each geomText row uses a different alpha-enabled color format",
        x = "",
        y = "Qualified leads"
    )
Out[5]:
18k 29k 41k 57k 63k 74k ALPHA ALPHA ALPHA ALPHA Jan Feb Mar Apr May Jun 0 20 40 60 80 Partner pipeline forecast Each geomText row uses a different alpha-enabled color format Qualified leads