Skip to content

Releases: JetBrains/lets-plot-kotlin

v4.13.0

20 Mar 20:37

Choose a tag to compare

[4.13.0] - 2026-03-20

This release is 100% compatible with Lets-Plot v 4.9.0,
GeoTools v 33.2

Added

  • Plot Annotations:

    • New labels parameter in geomSmooth() designed to display statistical summaries of the fitted model directly on the plot.
      This parameter accepts a smoothLabels() object, which provides access to model-specific variables like $R^2$, the regression equation and others [#1388].

      See: example notebook.

    • Plot tags. A tag can be specified via labs(tag = ...) and styled using theme parameters [#1407].

      See: example notebook and updated Plot Layout Diagrams.

    • Plot tags customization parameters in theme():

      • plotTag - sets the tag style via elementText().
      • plotTagLocation - specifies the area used for positioning the tag.
      • plotTagPosition - specifies the position of the tag within the selected area.
      • plotTagPrefix - text added before the tag value.
      • plotTagSuffix - text added after the tag value.

      See: example notebook.

  • Geometries:

    • New geomBracket(), geomBracketDodge() [#1114].

      See: example notebook.

    • geomImshow():

      • Support for custom colormaps [#780].

      • New cguide parameter: use to customize the colorbar for grayscale images.

        See: example notebook.

  • Color Scales:

    • New palette() method for color scales: generates a list of hex color codes that can be used with scaleColorManual() or scaleFillManual() to maintain consistent colors across multiple plots [#1444].

      See: example notebook.

    • New overflow parameter in scaleColorBrewer() / scaleFillBrewer(): controls how colors are generated when more colors are needed than the palette provides.
      Options: "interpolate" ("i"), "cycle" ("c"), "generate" ("g").

      See: example notebook.

  • Positional Scales:

  • gggrid(): interactive pan/zoom now propagates across subplots with shared axes (sharex / sharey) [#1413].

    See: example notebook.

  • Swing Apps:

    Support for plot default interactions and custom toolbar component.

    For more details, see Interactions demo in the "lets-plot-mini-apps" repository.

Changed

  • Kotlin: v2.2.20 (was v1.9.25).

  • Artifact changes in the core Lets-Plot library (v4.9.0):

    • New artifact for JVM Swing applications: org.jetbrains.lets-plot:lets-plot-swing.
      This artifact provides the SwingPlotPanel class, which can be used to display plots in Swing applications instead of the now-obsolete DefaultPlotPanelBatik.
      For details, see the jvm-swing-app example in the "lets-plot-mini-apps" repository.
    • [BREAKING]: Removed JavaFX artifacts.
      The org.jetbrains.lets-plot:lets-plot-jfx artifact is no longer available.
      Replace it with new org.jetbrains.lets-plot:lets-plot-swing dependency and use SwingPlotPanel instead of DefaultPlotPanelJfx.
      For details, see the jvm-javafx-app example in the "lets-plot-mini-apps" repository.
    • [BREAKING]: Removed plot-image-export module.
      The org.jetbrains.lets-plot:lets-plot-image-export artifact is no longer available.
      The PlotImageExport utility has been moved to the platf-awt module: org.jetbrains.letsPlot.awt.plot.PlotImageExport.
      The required org.jetbrains.lets-plot:platf-awt dependency is likely already present in your project.
  • [BREAKING]: ColorBrewer palettes: changed default behavior when the requested number of colors exceeds the palette's maximum size.
    Now defaults to "interpolate" for sequential/diverging palettes and "generate" for qualitative palettes.
    Previously, depending on the palette type, this either resulted in duplicate colors or random additional colors.
    Use the new overflow parameter to explicitly control this behavior.

  • Missing values in geomAreaRidges() create gaps in geometries instead of being interpolated over.

  • Discrete color scales (Brewer, Manual) now produce a colorbar guide when used with continuous data.
    Previously they produced a legend guide regardless of the data type.

Fixed

  • Drop commons-io dependency [#1421].
  • Unexpected replacement of double curly brackets with a single curly bracket [#1433].
  • geom_imshow: unclear error message when mixing transparencies [#1088].
  • geom_imshow and scale_y_reverse [#1210].
  • Nice to be able to get a list of colors from a color scale object [#1444].
  • Allow tooltips param to accept list [#1455].
  • Allow grouped tooltips for plots with multiple univariate geoms [#1460].
  • Fixed a regression in geomTextRepel() / geomLabelRepel(): points with empty labels were incorrectly skipped
    before building the repel obstacle set, so they were not included in collision avoidance and labels could overlap
    dense point clusters.

v4.12.1

22 Dec 23:59

Choose a tag to compare

[4.12.1] - 2025-12-22

This release is 100% compatible with Lets-Plot v 4.8.2,
GeoTools v 33.2

Added

  • Support for kotlin.time.Duration and java.time.Duration [LPK-294].

Fixed

  • Horizontal geom_boxplot() with alpha specified is displayed incorrectly [LPK-295].

v4.12.0

02 Dec 19:23

Choose a tag to compare

[4.12.0] - 2025-12-02

This release is 100% compatible with Lets-Plot v 4.8.1,
GeoTools v 33.2

Added

  • Geometries:

    • geomPointDensity() [#1370].

      See: example notebook.

    • Geoms with 1-to-1 statistics (such as geomQQ(), geomSina()) preserve the mapping to original data after statistical transformation.

      See: example notebook.

    • geomHistogram(): custom bin bounds (parameter breaks) [#1382].

      See: example notebook.

  • Plot Layout:

    • The legend automatically wraps to prevent overlap - up to 15 rows for vertical legends and 5 columns for horizontal ones [#1235].

      See: example notebook.

    • gggrid(): support for shared legends (parameter guides).

      See: example notebook.

  • Plot Theme:

    • flavorStandard() sets the theme's default color scheme [#1277].

      Use to override other flavors or to make defaults explicit.

      See: example notebook.

    • themeGray() as an alias for themeGrey().

    • New theme functions for setting legend justification: legendJustificationTop(), legendJustificationRight(),

      legendJustificationBottom(), and legendJustificationLeft().

      See: example notebook.

    • Support for inward axis ticks.

      See: example notebook.

  • Markdown:

    • Support for target attribute for links.
    • Links now open in a new tab by default [#1397].
  • ggtb(): sizeZoomin and sizeBasis parameters for geometry scaling [#1369].

    See: example notebook.

  • New output parameter for the library descriptor to control output types stored in notebook cells [LPK-277].
    Thanks to AndreiKingsley for the contribution!

      %use lets-plot(output="js, png")
    

    Available output types:

    • js - Classic Web output: HTML+JS
    • ktnb - Kotlin Notebook Swing-based rendering
    • svg - Static SVG output
    • png - Static PNG output

    Default: "js, ktnb, svg"

    Note: Static images (SVG/PNG) are hidden when js or ktnb outputs are present, and only displayed in environments where JavaScript is not executed (e.g., GitHub).

    This option can be helpful when a Kotlin Notebook file size becomes a problem.
    For example, when working with large datasets where plot interactivity is not a priority, storing only static output (SVG or PNG) can significantly reduce file size.

  • CenteredPlotPanel helper class for displaying plots centered in Swing applications.

Changed

  • [BREAKING] Explicit group aesthetic now overrides default grouping behavior instead of combining with it [#1401].

    See: example notebook.

Important

Previously, setting group="variable" would group by both the explicit variable AND any discrete
aesthetics (color, shape, etc.).
Now it groups ONLY by the explicit variable, matching ggplot2 behavior.
Use group=listOf(var1, var2, ...) to group by multiple variables explicitly,
and group=emptyList<Any>() to disable any grouping.

  • Missing values in geomLine(), geomPath(), geomRibbon(), and geomArea() create gaps in geometries instead of being interpolated over [LPK-269],[#818], [#1406].

    See: example notebook.

  • theme: the exponentFormat default value changed to "pow" - superscript powers of 10 (was e-notation).

  • The multi-layer line plot now shows tooltips for each series simultaneously, in the same way that a single-layer plot with color mapped to series does.

Fixed

  • Group by multiple columns [LPK-136].
  • geomPie on geospatial plot with mapJoin failes to render without explicit group aesthetic.
  • geomDensity2D: NullPointerException when weight aesthetic contains None values [#1399].
  • Tooltip shows duplicate lines when as_discrete is applied twice to the same var [#1400].
  • geomSina: incorrect shape in legend [#1403].
  • geomDensity2D: Incorrect processing of weighted statistics when None value occurs in the x or y column.
  • facetWrap: indescriptive error when the specified facet variable is not present in the dataset [#1409].
  • Integer numbers in facet strip titles are displayed as float [#1386].
  • Error when using scaleIdentity(aesthetic="shape") [#1212].
  • ggsave: theme option face="italic" doesn't work [#1391].
  • Fail early if string format is incorrect [#1410].
  • statECDF() takes a very long time for even moderately sized datasets [#1424].
  • inconsistencies in theme/flavor inheritance in gggrid() subplots.

v4.11.2

12 Sep 20:58

Choose a tag to compare

[4.11.2] - 2025-09-12

This release is 100% compatible with Lets-Plot v 4.7.3,
GeoTools v 33.2

Changed

  • ggsave(): large plot dimensions without units now require explicit unit specification.

    When plot size exceeds 20 without specifying units (e.g., ggsave(p, w=300, h=400)),

    we ask to specify units explicitly:

    ggsave(p, w=300, h=400, unit="px") or ggsave(p, w=3, h=4, unit="in").

Fixed

  • ggtb() support in Swing/Batik frontend [LPK-265].
  • Multiline support for axis labels in polar coordinates.
  • When the plot size in ggsave() is specified in pixels, dpi now affects

    only the physical size, not the pixel dimensions as before.
  • Blocking SwingUtilities.invokeAndWait() call on plot image export (JVM app)

v4.11.1

02 Sep 21:21

Choose a tag to compare

[4.11.1] - 2025-09-02

This release is 100% compatible with Lets-Plot v 4.7.2,
GeoTools v 33.2

Added

  • Facets Layout:

    • New strip_spacing, strip_spacing_x, and strip_spacing_y parameters in theme() to control spacing between the facet strip (title bar) and the plot panel.

    • New panel_spacing, panel_spacing_x, and panel_spacing_y parameters in theme() to control spacing between plot panels in faceted plots, [#1380].

      See: example notebook.

  • ggsave():

Changed

  • facetWrap() now drops factor levels that do not appear in the data (i.e., empty panels) by default [#1322].

    To keep unused factor levels, use the new drop parameter, i.e., drop=false.

    See: example notebook.

  • Facets Layout:

    • Removed extra spacing between facet strips and plot panels in facetWrap() and facetGrid().
  • ggsave(): removed Batik dependency (now uses lets-plot's built-in rasterizer).

Fixed

  • Tooltip for a line have higher priority than points, even if the point is specified first in the layer list [#1060].
  • geomRibbon(): tooltip appears in the wrong place on flipped ribbon [#1334].
  • Coordinate limits do not work on reversed scales [#1365]
  • Misaligned axis labels and ticks in polar coordinates.
  • Display order of fill categories not being set correctly in stacked plots? [#1367]
  • Unclear error when using geomRect with discrete scales [#1287]
  • xlim() breaks default scaleXDateTime() [#1348]
  • scaleXReverse breaks datetime formatting [#1257]
  • theme(plotTitle="blank") doesn't work with gggrid [#1349]
  • theme: error parsing color value pen [#1216]
  • Incorrect appearance of stacked density plot in polar coordinates [#1375]

v4.11.0

25 Jul 20:29

Choose a tag to compare

[4.11.0] - 2025-07-25

This release is 100% compatible with Lets-Plot v 4.7.0,
GeoTools v 33.2

Added

  • Time Series Plotting [#278],
    [discussion],
    [#678],
    [LPK-129]:

    • Support temporal data types from kotlinx.datetime, java.time, and java.util.
    • Support for timezone-aware java.time.ZonedDateTime and java.time.OffsetDateTime objects.

    See examples:

  • Geometries:

  • Layer Labels (Annotations):

    • Support in geomCrossbar()

      See: example notebook.

    • Support in waterfallPlot() via relativeLabels and absoluteLabels parameters.

      See: example notebook.

    • New inheritColor() option in annotations' configuration (see example notebooks above)

  • waterfallPlot() - support for combining waterfall bars with other geometry layers [#1344].

    See: example notebook.

  • Plot Layout:

    • New axisTextSpacing, axisTextSpacingX, and axisTextSpacingY parameters in theme() to control spacing between axis ticks and labels.
    • See new Plot Layout Diagrams showing various layout options and their effects on plot appearance.
  • More variants to specify a color by name:

    • all HTML/CSS colors;
    • various naming styles, e.g., "dark-gray", "darkgrey", "dark_grey", "DARKGRAY", etc.;
    • grayscale colors from "gray0" (black) to "gray100" (white);

    See the complete list of named colors.

  • sizeUnit parameter in geomPoint(), geomText/Label() and geomPie() accepts two new values: "min" and "max" [#260].

    "min" sets the size unit to the smaller of the unit steps along the x and y axes, while "max" sets it to the larger.

    This allows for more flexible relative sizing of points, pies and text in plots.

    See: example notebook.

Changed

  • Continuous data on discrete scales:

    Continuous data when used with discrete positional scales is no longer transformed to discrete data.

    Instead, it remains continuous, allowing for precise positioning of continuous elements relative to discrete ones.

    This resolves issues where combining discrete and continuous data in the same plot was difficult or impossible: [#1279].

    See: example notebook.

Tip

New way of handling continuous data on discrete scales could potentially break existing plots.
If you want to restore a broken plot to its original form, you can use the asDiscrete() function to annotate continuous data as discrete.

  • [BREAKING] geomBoxplot(): when y-oriented, it now uses aesthetics xlower/xmiddle/xupper instead of lower/middle/upper [#1319].
  • [BREAKING] waterfallPlot(): special "flow_type" value for label=elementText(color=...) replaced with "inherit". See label in the documentation.
  • [DEPRECATED] The positionDodgeV() function and the "dodgev" value for the position parameter are deprecated and will be removed in future releases.
  • Plot layout: reduced margins and spacing for title, caption, axes, and legend.
  • Updated RGB values for "lightgray" and "green". To restore the previous colors, use "gray75" and "lime", respectively.
  • waterfallPlot(): the appearance of the legend has been improved.
  • geomViolin(): tooltips are not shown in the centerline of the violin if showHalf != 0.
  • geomCrossbar(): the midline is not shown in the legend when fatten is set to 0, or when there is no mapping for it.
  • geomPointrange(): the midpoint will not be drawn if the y aesthetic is set to null.
  • geomBand(): the alpha aesthetic only affects the inner part of the geometry, as in geomRect().
  • geomBand(): show tooltip over the whole band, not just at the edges.

Fixed

  • AWT: plot prevents wheel events from bubbling up to the parent component.
  • geomBoxplot: unable to draw a y-oriented plot with stat = Stat.identity [#1319].
  • Can't add a layer which uses continuous data to a plot where other layers use discrete input [#1323].
  • Multiline legend labels were not vertically centered with their keys [#1331].
  • Poor alignment in legend between columns [#1332].
  • Ordered data was re-ordered by geomBoxplot [#1342].
  • Sec: CVE-2024-47554 (commons-io) [#1231]
  • java.util.zip.ZipException: duplicate entry: letsPlotKotlinAPI/version.properties [#279]

v4.10.0

20 Mar 21:17

Choose a tag to compare

[4.10.0] - 2025-03-20

This release is 100% compatible with Lets-Plot v 4.6.1,
GeoTools v 32.1

Added

  • Grouping plots:

  • Geometries:

  • Texts and labels:

  • In the nudge position adjustment -
    the ability to specify absolute shift values for nudge:

    • unit parameter in positionNudge().
    • nudgeUnit parameter in geomText() and geomLabel()

    See example notebook.

  • Theme:

    • Parameters legendTicks and legendTicksLength for fine-grained control over colorbar tick marks [#1262].

      See: example notebook.

  • Aliases for transparent color: "" (blank string), "blank" and "transparent" color [#1281].

Changed

  • [DEPRECATED] class GGBunch is deprecated. Please use ggbunch() function instead.
  • [BREAKING] The height parameter has been deprecated for the geomErrorBar.
  • Axis breaks: the minimum distance between axis labels was reduced to avoid unsuitable layouts.
  • Axis breaks: changed default lower_exp_bound to -7 and upper_exp_bound to 6 (same as in theme(...)).
  • Axis labels: changed default justification for rotated labels.
  • Axis labels: changed orientation of automatic vertical labels.
  • [BREAKING] geoms tile, bin2d, contour, contourf, density2d, density2df : default coordinate system changed from 'fixed' to 'cartesian'.
  • [BREAKING] tiles in geomBin2d() are sized according to the binwidth value, not the maximum possible.
  • [BREAKING] In Lets-Plot JS API v 4.6.0 (only Lets-Plot/JS users): signature of "buildPlotFromXXX" functions changed, see current Kotlin/JS declarations:
  • [BREAKING] In Lets-Plot v 4.6.0, Maven artifacts:
    • artifacts platf-awt, platf-batik, platf-jfx-swing are no longer published with a "-jvm" suffix.
      Before, these artifacts could be used in dependencies either with or without the "-jvm" suffix. Now only without suffix.
    • artifact "org.jetbrains.lets-plot:deprecated-in-v4-jvm" is removed.

Fixed

  • Incorrectly rendered Area chart [#1295].
  • Tooltip should not cover and hide the geometry that it provides info for [#1275].
  • General purpose scaleContinuous: can't use the expand parameter [#1285].
  • Error when using stat='summary' if the data contains NaN values [#1301].
  • Broken plotBackground in gggrid [#1124].
  • plotBackground not inheriting from rect [#1278]
  • gggrid: allow title and other labels for the entire figure [#715].
  • ggbunch: overall title [#321].
  • Expand discrete axis according to tile size with geomTile [#1284].

v4.9.3

16 Dec 22:36

Choose a tag to compare

[4.9.3] - 2024-12-16

This release is 100% compatible with Lets-Plot v 4.5.2,
GeoTools v 32.1

Changed

  • Number formatting:
    • We have aligned our specifications with D3.js (rather than Python): g format type with ~ (e.g. ~g) now truncates trailing zeros, and without ~ it doesn't.
    • The default number formatter now respects theme(exponent_format=...) settings and does not use 's' format type for large numbers.
  • Legends with more than 200 items are now not rendered.

Fixed

  • Bad precision in the default tooltip format when using coord limits [#1134].
  • Display integer values without fractional part in tooltips [#1186].
  • Suboptimal tooltip positioning in facets [#1187].
  • Incorrect Y-axis layout with facets and panel_inset [#1194].
  • Grouped plot hits the limit of 1000 and doesn't render with not-friendly exception [#1224].
  • Kandy toPNG reports NullPointerException [#1228]
  • Wrong formatting when type='g' for small values [#1238].
  • Formatting when type='g' for large values throws exception [#1239].
  • Wrong formatting when type='s' with explicit precision [#1240].
  • Extra trim in formatted number when type='g' [#1241].
  • Axis breaks are badly formatted if explicitly set [#1245].
  • Badly formatted zero break for the "~g" format [#1246].
  • How to adjust the vertical position of geom_text when using position_dodge [#1248].
  • Incorrect result for format(9.999, ".2f") [#1251].
  • Tooltips overlapping when not enough vertical space for them [#1254].
  • Plot limited to 1000 lines (Internal error: IllegalStateException : Too many groups: 1099) [#1261].
  • Nice to have tooltip to inherit formatting configured for axis [LPK-229].

v4.9.2

19 Nov 18:36

Choose a tag to compare

[4.9.2] - 2024-11-19

Same as 4.9.1, added missing Maven artifacts.

v4.9.1

18 Nov 22:04

Choose a tag to compare

[4.9.1] - 2024-11-18

This release is 100% compatible with Lets-Plot v 4.5.1,
GeoTools v 32.1

Changed

  • Migrated to new Kotlin Jupyter integration.