Bokeh 1.0 Released

/ Bryan Van de Ven

<clears throat> Well… here we are at last.

We are pleased to announce the release of Bokeh 1.0!

Overall Bokeh highlights are:

  • Powerful Bokeh Server — “Shiny For Python”
    • Affords Python and Javascript callbacks for data updates and UI events
    • Connects PyData tools to web dashboards with plot tools and HTML widgets
    • Uses an efficient protocol for streaming and array data
  • Highly customizable plotting API, including graphs, hex tiles, and maps
  • Full support for Jupyter Notebooks and Jupyter Lab

Specific issues completed for this release include:

  • New glyph for MultiPolygons with holes #2321
  • Fixes and improvements to DataTable #6454, #7116, #7417, #8021, #8040, #8050, #8201
  • CustomAction for user-defined Toolbar buttons #8099
  • Plain JSON export/embed functions #5231
  • Toolbar autohide property to hide toolbars when not in use #8284
  • Callback to allow AjaxDataSource to adapt JSON responses #8321
  • Scatter glyph for parameterizable marker type #5884
  • Reuse webdrivers for faster PNG/SVG export by default #8329
  • Improved testing capabilities #2596, #8078, #8139, #8146, #8217, #8225
  • Quicker import times #8309

Plus several other bug fixes and documentation improvements. For full details, see the CHANGELOG and Release Notes. Additionally, examples for each release are uploaded to CDN. Click here to download.

If you are using Anaconda, Bokeh can most easily be installed by executing the command conda install -c bokeh bokeh . Otherwise, use pip install bokeh.

What 1.0 Means

First off, what “1.0” does not mean: It does not mean that all the work is done, or that all bugs are squashed. For instance, there are still a fair number of known issues with the built-in layout system. A large Work In Progress PR will fix many of these issues in a future 1.1 release. Additionally, you can see larger “roadmap” items still TBD in the new Vision and Work section of the docs.

But these things will be done in a way compatible with current APIs. After a period of youthful “0.x” experimentation, the Core Team asserts that existing public APIs will now be defended. (*) From now on, any intentional breaking changes will precipitate a major version bump. Of course we will strive to avoid any unintentional changes as well—see the section below on improved testing. This commitment is the major purpose of releasing “1.0” now.

This milestone also represents the progress of making Bokeh a truly independent project in the context of a wider OSS community. However, to secure long-term success, the Bokeh project must diversify both its contributor base and its funding sources. So if you or your organization find Bokeh valuable, please consider making a concrete contribution towards sustaining the project. This could be by making a financial donation through NumFOCUS or by getting involved with development.

Long story short: right now marks a fantastic time to get more involved.

(*) This applies without qualification to all public Python APIs. The public/private distinction for BohehJS is still somewhat murky. Our specific commitment around BokehJS is this:

Any and all usage in currently published “CustomJS” or custom extension examples will continue to function without change, and synchronizable BokehJS models and events (e.g. “ColumnDataSource” or “Tap”), will not be removed or have their existing properties modified..

In order to make improvements and bug fixes, we will continue to use discretion regarding any changes in other, low level BokehJS code that is not intended for general users, and we will work to make these BokehJS API boundaries more clear as time goes on. </small>


Before getting to the technical details of this release, I wanted to take a moment to recognize some stats about our community:

  • The public mailing list has more than 1000 members
  • @bokeh has over 8000 followers
  • The number of GitHub stars just hit 8300
  • Combined conda+pip installs regularly exceed 600k/month (!)
  • The docs site has 55-60k unique monthly visitors.

Any one of these facts is humbling, but the current success of Bokeh is owed to the work of very many contributors. Please be sure to read all the way to the end, and the full “Thank You” section below.

Patches With Holes

Thanks to the efforts of Julia Signell, old issue #2321 - Patches with Holes has been completed. The approach taken was to add a new glyph type MultiPolygons that is inspired heavily by GeoJSON format of sub-polygons that specify an “exterior ring” followed by optional “holes” inside the exterior ring. (This approach was also inspired by earlier work by Sarah Bird.) Using this glyph from the bokeh.plotting API looks like:


        # first multi-polygon (blue)
        [[ [1, 1, 2, 2],
           [1.2, 1.6, 1.6],
           [1.8, 1.8, 1.6]], # sub-polygon with 2 holes

         [ [3, 3, 4] ], # sub-polygon with no holes

        # second multi-polygon (red)
        [[ [1, 2, 2, 1],
           [1.3, 1.3, 1.7, 1.7] ]], # sub-polygon with 1 hole
        [[ [4, 3, 3, 4], [3.2, 3.2, 3.6], [3.4, 3.8, 3.8] ], [ [1, 3, 1] ]],
        [[ [1, 1, 2, 2], [1.3, 1.7, 1.7, 1.3] ]]],
    color=['navy', 'crimson'])

The resulting output has two multi-polygons, some with holes and some without:

Image of MultiPolygon

These “patches with holes” are often useful for working with GIS data or maps, and support all the usual and expected hover and hit-testing interactions. In the future, we plan to add more convenience methods to integrate directly with GeoJSON sources.

Another use case is filled contour plots. Although it currently takes some effort to create the necessary coordinates from single iso-lines, it is possible to generate filled contour plots for the first time:

Image of contour plot

As resources permit, we also plan to add methods to make generating filled contours plots simpler. (This would also make a great self-contained project for an interested new contributor!)

A New Scatter

Another old issue #5884 - Let Scatter marker type be parameterizable made it into the 1.0 release. Now the scatter glyph method creates a new Scatter object, that can specify the marker type of each data point individually:

plot.scatter(x=[1,2,3], y=[4,5,6], marker=['circle', 'hex', 'square'])

This capability is especially useful together with a new factor_marker transform that can map categorical values to marker types:

SPECIES = ['setosa', 'versicolor', 'virginica']
MARKERS = ['circle', 'diamond', 'triangle']

p = figure(title = "Iris Morphology")

p.scatter("petal_length", "petal_width", source=flowers, legend="species",
          fill_alpha=0.4, size=12,
          marker=factor_marker('species', MARKERS, SPECIES)
          color=factor_cmap('species', 'Category10_3', SPECIES))

Image of Scatter markers

This approach with a parameterized scatter is useful whenever you want to keep all the data inside a single ColumnDataSource.

JSON Export and Embed

Bokeh 1.0 adds a new function to bokeh.embed:


This function can be called on any Bokeh object, e.g plots or layouts, and the output of the call is a block of JSON that represents a Bokeh Document for obj. (You might also note that Bokeh now uses “simple” IDs by default):

{'target_id': None,
 'root_id': '1002',
 'doc': {'roots': {
   'references': [
      {'type': 'PanTool',     'id': '1022', 'attributes': {}},
      {'type': 'BasicTicker', 'id': '1013', 'attributes': {}},
      {'type': 'LinearAxis',  'id': '1017', 'attributes': {
        'axis_label': 'petal_length',
        'formatter': {'id': '1043', 'type': 'BasicTickFormatter'},
        'plot': {'id': '1002', 'subtype': 'Figure', 'type': 'Plot'},
        'ticker': {'id': '1018', 'type': 'BasicTicker'}}},
      {'type': 'DataRange1d', 'id': '1004', 'attributes': {'callback': None}},
      {'type': 'Circle',      'id': '1038', 'attributes': {'fill_alpha': {'value': 0.2},
        'fill_color': {'field': 'fill_color'},
        'line_color': {'field': 'line_color'},
        'size': {'units': 'screen', 'value': 10}
        'x': {'field': 'x'},
        'y': {'field': 'y'}}},


      {'type': 'BoxZoomTool', 'id': '1024', 'attributes': {
        'overlay': {'id': '1030', 'type': 'BoxAnnotation'}}}
   'root_ids': ['1002']},
  'title': '',
  'version': '1.0.0'}}

Now you can use this JSON output in any HTML document by calling a single function from JavaScript: Bokeh.embed.embed_item(item, "myplot") The first parameter is the JSON output and the second parameter is the id of the div to embed the content into. (It’s also possible to specify the id on the Python side instead if that is preferable.)

As a concrete example, if you have a Flask application that serves Bokeh JSON at an endpoint /plot like this:

from bokeh.embed import json_item

def plot2():
    p = make_plot('sepal_width', 'sepal_length')
    return json.dumps(json_item(p))

That content can be embedded from a fetch call in the page template like this:

  <div id="myplot"></div>

    .then(function(response) { return response.json(); })
    .then(function(item) { Bokeh.embed.embed_item(item, "myplot"); })

Testing, Testing…

Thanks to a few Pull Requests from recent new contributor xavArtley, Bokeh unit tests now run continously on Windows for the first time ever:

Image of Appveyor

Another development concerns rigorous UI testing. Sometime in 2017 our previous Selenium integration testing infrastructure fell into disrepair. As a result unfortunate regressions and bugs started to slip past our review. I am happy to report that an important goal for 1.0 has been acheived, and our Selenium integration testing machinery has been re-built from the ground up, as well as greatly expanded. Where we previously only had roughly a dozen UI tests, now almost 200 Selenium tests run continously to explicitly exercise various Bokeh features and behaviors:

Image of Selenium tests

These are both very important improvements to our testing capability that will help us to ensure stability in 1.x releases. Still, they are only the beginning. With the new infrastructure it is easier than ever to add these kinds of tests, and we plan to do just that!

Examples Refresh

Leading up to this release we also tried to make a pass over all the examples in the documentation. There are several hundred examples available now, and many were in need of some gentle attention. All examples were reviewed to improve their visual style, and to make them reflect current best practices.

Additionally, some new examples were added. One worth mentioning in particular is a face dectection demo written by Luke Canavan. Here it is, identifying me (but not our dog):

Image of faces example

This is a nice example because it ties together several great Bokeh features, all in under 80 lines of Python code.

  • Demonstrates data updates and streaming API
  • Connects web visualization to PyData tools (OpenCV)
  • Uses custom template for nice look and responsive layout

This example is located at in the GitHub repository under examples/app/faces. To run it, you will need OpenCV and the latest Bokeh sample data installed.

Gracias धन्यवाद Cпасибо 谢谢 Obrigado شكرا Danke ありがとう Merci

I’d like to thank the 321 GitHub contributors who have helped make Bokeh such an amazing project to date, personally, and on behalf of the entire Core Team.

If you are one of these 321 people who have made a PR up until now, we have a special “pre 1.0 contributor” laptop sticker just for you, as a small token of appreciation. Reach out on Gitter chat and we will figure out how to send it to you!

In addition, I’d like to personally recognize a few individuals who have made a profound impact on the development of Bokeh over the last five years:

  • Damian Avila
  • Sarah Bird
  • Luke Canavan
  • Brendan Collins
  • Maggie Mari
  • Paddy Mullen
  • Mateusz Paprocki
  • Havoc Pennington
  • Fabio Pliger
  • Philipp Rudiger
  • Julia Signell
  • Jean-Luc Stevens
  • Hugo Shi
  • Claire Teng
  • Peter Wang
  • Ben Zaitlen

It has been my joy and privilege to work with every single one of them.

Beyond all these folks, I’d also like to thank everyone who has made issues, provided valuable feedback or ideas for improvements, helped answer questions on the mailing list or Stack Overflow, or supported the Bokeh community in any capacity.

As always, anyone interested in helping out should drop by the Dev Chat Channel! and say hello!


Bryan Van de Ven