<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Vitessce Blog</title>
        <link>http://vitessce.io/docs/2025-08-05/30b1ac11/blog/</link>
        <description>Vitessce Blog</description>
        <lastBuildDate>Tue, 05 Sep 2023 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Next-generation spatial views]]></title>
            <link>http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/</link>
            <guid>http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/</guid>
            <pubDate>Tue, 05 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[The next generation of spatial and layerController views in Vitessce is ready for testing.]]></description>
            <content:encoded><![CDATA[<p>The next generation of spatial and layerController views in Vitessce is ready for testing.
There are several motivations for re-implementing these views:</p>
<ul>
<li>Support a more flexible coordination model</li>
<li>Support multi-channel segmentation bitmasks</li>
<li>Support per-obsType segmentation bitmasks</li>
<li>Use semantically-meaningful data types for points and spots</li>
<li>Unify the visualization properties for polygon and bitmask segmentations</li>
</ul>
<p>These views are available in JS package v3.2.0 as <code>spatialBeta</code> and <code>layerControllerBeta</code>.</p>
<p>Note: automatic initialization of the coordination space is not yet supported.
We suggest using the object-oriented <code>VitessceConfig</code> API (and the new <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#linkviewsbyobjectviews-obj-metatrue">linkViewsByObject</a> method) to manually initialize the values.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="flexible-coordination-model">Flexible coordination model<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/#flexible-coordination-model" class="hash-link" aria-label="Direct link to Flexible coordination model" title="Direct link to Flexible coordination model">​</a></h2>
<p>Previously, the spatial view used two coordination types <code>spatialSegmentationLayer</code> and <code>spatialImageLayer</code> which required an array of nested objects as values.
Usage of nested objects as coordination values prevents coordination of a subset of those values.
For example, this prevented coordinating channel colors while not coordinating other properties such as channel intensity windows.</p>
<p>In contrast, <code>spatialBeta</code> and <code>layerControllerBeta</code> are more flexible because they use multi-level- and meta- coordination to achieve the configuration of hierarchical properties including image layers and channels.</p>
<p>In the base case of the single-level coordination model, each view can be mapped to a set of coordination scopes, one scope per coordination type.
Multi-level coordination extends this model by allowing each coordination scope to itself have its own set of coordination scope mapping, generating a new "level" of coordination.</p>
<p>For example, it allows each image layer coordination scope to be mapped to its own coordinated properties such as opacity and visibility.
Meanwhile, the opacity and visibility properties remain primitive numeric and boolean values, respectively, which are agnostic to the existence of any image layer.</p>
<p>Other benefits:</p>
<ul>
<li>Ability to configure global Z/T/C indices for coordination of spatial/temporal/channel selections across data types, datasets, or views</li>
<li>Ability to configure 3D vs. 2D mode globally (i.e., per-view, as opposed to per-image layer).</li>
<li>Ability to fall back to coordination values in the previous level (if not initialized in the subsequent level).</li>
</ul>
<p>A disadvantage of this more flexible coordination model is that it requires a more complex JSON representation that is tedious to write and reason about.
To address this, we have extended our object-oriented configuration APIs:</p>
<ul>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#linkviewsbyobjectviews-obj-metatrue">VitessceConfig.linkViewsByObject</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#addcoordinationbyobjectobj">VitessceConfig.addCoordinationByObject</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#addmetacoordination">VitessceConfig.addMetaCoordination</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#usecoordinationbyobjectobj">VitessceConfigView.useCoordinationByObject</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#usemetacoordinationmetascope">VitessceConfigView.useMetaCoordination</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#vitessceconfigmetacoordinationscope">VitessceConfigMetaCoordinationScope</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#coordinationlevel">CoordinationLevel</a></li>
</ul>
<p>The resulting <code>VitessceConfig</code> instance can be converted <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/#tojson">to JSON</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="multi-channel-per-obstype-segmentation-bitmasks">Multi-channel, per-obsType segmentation bitmasks<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/#multi-channel-per-obstype-segmentation-bitmasks" class="hash-link" aria-label="Direct link to Multi-channel, per-obsType segmentation bitmasks" title="Direct link to Multi-channel, per-obsType segmentation bitmasks">​</a></h2>
<p>The flexible coordination model described above enables data-related properties such as <code>obsType</code> ("observation type") to be mapped per-image-channel.
In other words, for segmentation bitmask images, each bitmask channel can represent a different type of observation.
For example, channel zero can contain nucleus segmentations, while channel one contains cell segmentations, and channel two contains glomerulus (or other functional tissue unit) segmentations.</p>
<p>Thus, <code>obsType</code> can be used to join multiple data types together, as described in our <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/">previous blog post</a> (but now on a per-channel basis).
For example, the <code>obsType: 'cell'</code> channel might have its own <code>obsSets</code> for cell type annotations,
while the <code>obsType: 'glomerulus'</code> channel has its own <code>obsSets</code> for annotation of which glomeruli are globally sclerotic vs. non-globally sclerotic.</p>
<p>For more details about data storage, see <a href="https://github.com/keller-mark/data-conventions/tree/main/multi-channel-label-images" target="_blank" rel="noopener noreferrer">data-conventions/multi-channel-label-images</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="obspoints-and-obsspots-data-types">obsPoints and obsSpots data types<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/#obspoints-and-obsspots-data-types" class="hash-link" aria-label="Direct link to obsPoints and obsSpots data types" title="Direct link to obsPoints and obsSpots data types">​</a></h2>
<p>The new data types <code>obsPoints</code> and <code>obsSpots</code> enable more straightforward configuration of visualizations containing point-based measurements (e.g., MERFISH transcript points) and spot-based measurements (e.g., Visium spots).
While the data representations for points and spots are currently identical (XY coordinates), the more semantically-meaningful nature of the data type names helps to infer (from the Vitessce configuration) how each should be visualized.
For example, points have a fixed size in pixels as opposed to a physical size (i.e., "dimensionless"), while spots have a configurable physical size which is reflected by changes in pixel size during zoom interactions.</p>
<p>These data types also more closely reflect SpatialData's point and (circle) shape <a href="https://spatialdata.scverse.org/en/latest/design_doc.html#elements" target="_blank" rel="noopener noreferrer">Elements</a>, which will make <a href="https://github.com/vitessce/vitessce/issues/1292" target="_blank" rel="noopener noreferrer">SpatialData support</a> more straightforward to implement.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="unified-visualization-properties-for-segmentations">Unified visualization properties for segmentations<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/#unified-visualization-properties-for-segmentations" class="hash-link" aria-label="Direct link to Unified visualization properties for segmentations" title="Direct link to Unified visualization properties for segmentations">​</a></h2>
<p>Vitessce supports both polygon- and bitmask-based segmentations.
Previously, these different types of segmentations had to be configured differently, with polygon segmentations supporting a custom object like</p>
<div class="language-json codeBlockContainer_HF71 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OR8B"><pre tabindex="0" class="prism-code language-json codeBlock_fq9o thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_oXbW"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> opacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> radius</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> visible</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> stroked </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>while bitmask segmentations supported same nested array of image properties as the <code>image</code> data type.
In addition to exposing details about the internal implementations of the visualizations, these different configurations added complexity to the controls for segmentations, making them dependent on knowledge of the segmentation type.
In part, these differences were related to the fact that the previous spatial view did not support a <code>stroked: true/false</code> property for bitmask-based segmentations.
This limitation led to the need to pre-compute and redundantly store both filled and stroked segmentations:</p>
<p><img decoding="async" loading="lazy" alt="Filled and stroked (&amp;quot;boundaries&amp;quot;) cell segmentations" src="http://vitessce.io/docs/2025-08-05/30b1ac11/assets/images/segmentation_boundaries_old-055fe5006d2a5187c10e9853b4b6d9e4.png" width="1738" height="902" class="img_MiZr"></p>
<p>The new spatial view implementation supports on-the-fly <code>stroked: true/false</code> for bitmask-based segmentations, with configurable stroke width.
Using multi-level coordination, the same visual properties can be configured for both polygons and bitmasks.
The boundaries of bitmask observations are computed on the GPU using a custom DeckGL layer.</p>
<p><img decoding="async" loading="lazy" alt="Filled and stroked (&amp;quot;boundaries&amp;quot;) cell segmentations" src="http://vitessce.io/docs/2025-08-05/30b1ac11/assets/images/segmentation_boundaries_new-2635de3ca0b471ecd93af200b3802147.png" width="1788" height="858" class="img_MiZr"></p>
<p>Note: the GPU-based boundary visualization is based on a heuristic and is not perfect.
Parts of bitmask boundaries that are close to image tile boundaries have the potential to be under-drawn (because the shader only has access to the data for one tile at a time).
Boundaries that are concave may also be under-drawn.
However, the boundary will never be over-drawn (i.e., pixels will not be rendered that would lie outside of the boundary if it had been rendered perfectly).
The imperfections are more noticible at higher zoom levels.
In the future, strategies such as multi-pass rendering of staggered tiles could resolve these limitations.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="demos">Demos<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/#demos" class="hash-link" aria-label="Direct link to Demos" title="Direct link to Demos">​</a></h2>
<p>We have configured several examples which make use of the <code>spatialBeta</code> and <code>layerControllerBeta</code> views:</p>
<ul>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=visium-2023-image-only">RGB image</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=blin-2019-2">Multiplex + 3D image</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=blin-2019-3">Comparison of volumetric rendering algorithms</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=visium-2023">Visium with obsSpots data type</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=codeluppi-2018-2">osmFISH with obsPoints data type</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=codex-2023">Bitmask-based obsSegmentations for cells and nuclei</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=kpmp-2023">Bitmask-based obsSegmentations for functional tissue units</a></li>
<li><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/#?dataset=kpmp-2023-2">Bitmask-based obsSegmentations for functional tissue units with coordinated opacity</a></li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="usage-from-python">Usage from Python<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/#usage-from-python" class="hash-link" aria-label="Direct link to Usage from Python" title="Direct link to Usage from Python">​</a></h2>
<p>We have implemented object-oriented APIs for the multi-level coordination model in both JavaScript and Python.
The equivalent of the <code>linkViewsByObject</code> function from JS is the <code>link_views_by_dict</code> function in Python.
This enables initialization of the coordination space using the same patterns in both languages.
Jupyter notebooks demonstrating usage of the Python API are available in the documentation website for the vitessce Python package.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="next-steps">Next steps<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/spatial-beta/#next-steps" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps">​</a></h2>
<p>There are some remaining features to implement, including a mechanism for automatic initialization of the multi-level coordination values based on the data configuration.
If testing goes smoothly, we plan to replace the current <code>spatial</code> and <code>layerController</code> views with their Beta counterparts.
We would do this in a new major version due to the UI differences, but this would be implemented as a non-breaking change with automatic upgrade functions to internally convert previously-configured visualizations (in the same way we have implemented upgrade functions for all prior introductions of new config schema versions).</p>]]></content:encoded>
            <category>spatial</category>
        </item>
        <item>
            <title><![CDATA[Internal code refactoring]]></title>
            <link>http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/</link>
            <guid>http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/</guid>
            <pubDate>Thu, 13 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Vitessce has a relatively large and complex code base for a JavaScript project.]]></description>
            <content:encoded><![CDATA[<p>Vitessce has a relatively large and complex code base for a JavaScript project.
Like many other JavaScript projects, we not only develop a library for distribution on NPM but also an internal development web application and a documentation website.
This alone presents many challenges for JavaScript build tools: namely hot module reloading in development plus a single-file bundle for NPM.
Often additionally, we want to produce a matrix of different builds: minified/unminified, development/production, esm/umd/commonjs.
If we have any custom CSS that goes along with our JavaScript, we also need to think about how that will get included in the bundle.</p>
<p>However, in Vitessce, many other things complicate both the development and production setups.</p>
<p>Luckily, there are new ways to address these complications due to improvements in language standards and build tooling.
Thanks to discussions with <a href="http://gehlenborglab.org/team/members/trevor-manz/" target="_blank" rel="noopener noreferrer">Trevor Manz</a>, we have been able to greatly improve our build setup.</p>
<p>In the following sections I describe each requirement and how we approached it in the new setup, in the hopes that it may be useful to others.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="previous-build-setup">Previous build setup<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#previous-build-setup" class="hash-link" aria-label="Direct link to Previous build setup" title="Direct link to Previous build setup">​</a></h2>
<p>We previously used ejected <a href="https://create-react-app.dev/" target="_blank" rel="noopener noreferrer">create-react-app</a> (CRA) scripts. The base CRA scripts work well for publishing web apps but are not meant for bundling a library.
We had to eject the base scripts to add support for the library build.
The resulting scripts consisted of two Webpack configurations which used many plugins that were difficult to maintain but supported many of our requirements.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="failed-attempts">Failed attempts<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#failed-attempts" class="hash-link" aria-label="Direct link to Failed attempts" title="Direct link to Failed attempts">​</a></h2>
<p>While I previously made attempts to simplify our bundle scripts by using <a href="https://rollupjs.org/" target="_blank" rel="noopener noreferrer">Rollup</a> or <a href="https://vitejs.dev/" target="_blank" rel="noopener noreferrer">Vite</a> in place of CRA, these efforts were never successful.
They usually required hours of debugging to find a perfect (ordered) set of plugins and parameter settings to support all of the requirements of Vitessce and its dependencies.
It often felt like a game of whack-a-mole, fixing each issue just for another to emerge.
After days of debugging, I achieved fully working setups on a few occasions.
Unfortunately, even on these successful occasions (i.e., bundles, tests, and development server with no errors), I would still encounter issues preventing full adoption: the bundle size would be larger, or hot reloading in the development server would be unacceptably slow.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="incentives-for-this-work">Incentives for this work<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#incentives-for-this-work" class="hash-link" aria-label="Direct link to Incentives for this work" title="Direct link to Incentives for this work">​</a></h2>
<p>As a graduate student writing academic software, there is little external incentive to spend time to improve these build processes (or address tech debt in general), as they take time away from the implementation of new features.
It is difficult to communicate to others the current problems and the benefits that a smaller, faster, modular, or more modern JavaScript bundle would enable.
It is not possible to quantify how many people <em>didn't</em> use your package because the bundle size was too large or its format was not compatible with direct inclusion in an HTML page via CDN.</p>
<p>Further, it is difficult to estimate how long this work will take.
It often requires major code refactoring, during which any unrelated code changes by other team members will need to be re-implemented in the refactored code base.
For external consumers of the NPM package, bundle structure or format changes often constitute breaking changes.
All of these factors mean that this work is often perfomed outside of normal work hours so that it does not prevent the development of new features or conflict with the work of other team members.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="the-problems">The problems<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#the-problems" class="hash-link" aria-label="Direct link to The problems" title="Direct link to The problems">​</a></h2>
<p>Our previous bundle was very large at 127 MB.
The <a href="https://unpkg.com/" target="_blank" rel="noopener noreferrer">Unpkg</a> CDN refuses to serve files from packages this large, preventing direct usage in HTML files and sites like <a href="https://observablehq.com/" target="_blank" rel="noopener noreferrer">ObservableHQ</a>.
Further, this prevents dynamically loading Vitessce from CDN for our Python or R widgets, for example.
For library consumers, our unminified UMD build was 32.7 MB, slowing down their build processes and bloating their bundles.
For end users, such a large bundle of JavaScript increases network usage and slows down page load times.
For potential library adopters, seeing these numbers on NPM adds a sense of sticker-shock that could prevent adoption.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="new-build-setup-by-requirement">New build setup, by requirement<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#new-build-setup-by-requirement" class="hash-link" aria-label="Direct link to New build setup, by requirement" title="Direct link to New build setup, by requirement">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="bundle-size-and-individual-component-bundles">Bundle size and individual component bundles<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#bundle-size-and-individual-component-bundles" class="hash-link" aria-label="Direct link to Bundle size and individual component bundles" title="Direct link to Bundle size and individual component bundles">​</a></h3>
<p>One factor contributing to the overall bundle size is that we also included self-contained builds for individual React components.
With such a large main bundle file, library consumers who only want to use individual components can achieve better development performance in their own environments by directly importing from the individual component bundles.
However, this requirement adds redundant code to the bundle and contributes to the overall bundle size on NPM, as each individual component also requires a matrix of builds (minified/unminified, development/production, etc).</p>
<p>We addressed this by adopting a "monorepo" repository structure.
This way, we can implement individual components in their own sub-packages which can be published to NPM as standalone bundles.
Practically, this meant using <a href="https://pnpm.io/" target="_blank" rel="noopener noreferrer">PNPM</a> for dependency management (as opposed to NPM), which implements many features for monorepo ("workspaces") management, including recursive installation and parallel script running.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="minimizing-bundle-steps-in-sub-packages">Minimizing bundle steps in sub-packages<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#minimizing-bundle-steps-in-sub-packages" class="hash-link" aria-label="Direct link to Minimizing bundle steps in sub-packages" title="Direct link to Minimizing bundle steps in sub-packages">​</a></h3>
<p>As browser, NodeJS, and JavaScript module standards have matured, there is becoming less of a need for a complex bundle step in between source code development and published code deployment, as described <a href="https://cmdcolin.github.io/posts/2022-05-27-youmaynotneedabundler" target="_blank" rel="noopener noreferrer">by others</a>.
In short, when the consumer of your library is another JavaScript library, it  makes more sense for them to import from the original source code.
On the other hand, when the consumer of your library is an HTML page, it currently makes more sense for them to import a single-file, minified bundle.
As browser module features like <a href="https://github.com/WICG/import-maps" target="_blank" rel="noopener noreferrer">importmaps</a> improve, perhaps these considerations will evolve.</p>
<p>In the meantime, bundlers can be useful for generating the single-file, minified bundles used in the browser.
Historically, another role of a bundler has been to resolve non-JS and non-standard file imports.
For instance, it has become common to want to import CSS, SCSS, SVG, JSON, GLSL, and Web Worker files via JS-like syntax (e.g., <code>import obj from './file.json'</code>).
But such non-JS import statements are not supported in the JavaScript standard and therefore will result in an error when run in a browser.
A bundler provides the ability to transform non-standard source code into browser-compatible code.</p>
<p>On the other hand, in order to publish re-usable source code to NPM and avoid bundle steps, we need to avoid these non-standard imports.
We have achieved this by using only <code>tsc</code> as a simple build step in our sub-packages, with only minor exceptions.
Where there are exceptions, the separation of concerns enabled by the monorepo structure allows more complex bundle steps to be scoped to the sub-repo code and its requirements, reducing complexity, improving performance, and allowing different bundler tools to be used in each context.
For example, we make exceptions in the following places:</p>
<ul>
<li>The main <code>vitessce</code> package: For both backwards compatibility and to enable inclusion in HTML pages, we generate a matrix of single-file bundles using Vite and its library mode.</li>
<li>The <code>@vitessce/icons</code> sub-package: To enable SVGs to be imported and transformed into React components automatically, we use Vite and <a href="https://www.npmjs.com/package/vite-plugin-svgr" target="_blank" rel="noopener noreferrer">vite-plugin-svgr</a> to produce a plain JS bundle that can be consumed by other monorepo packages.
(Note: This setup does not allow SVGs to be hot-reloaded during development, but we can probably assume they will not change frequently.)</li>
<li>The <code>@vitessce/workers</code> sub-package: To enable Web Workers to be in-lined, we use Rollup + <a href="https://www.npmjs.com/package/rollup-plugin-web-worker-loader" target="_blank" rel="noopener noreferrer">rollup-plugin-web-worker-loader</a>. (This comes with the same hot reloading caveat as the <code>@vitessce/icons</code> case above.)</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="website-development-with-hot-reloading">Website development with hot reloading<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#website-development-with-hot-reloading" class="hash-link" aria-label="Direct link to Website development with hot reloading" title="Direct link to Website development with hot reloading">​</a></h3>
<p>We have two website requirements in this project: a minimal development website (an <code>index.html</code> page with a list of demos), and a documentation website.
These websites both need to consume the library code, ideally with hot reloading support during development.</p>
<p>In the same way that the monorepo's separation of concerns facilitates different bundling tools scoped to each sub-repo, it also enables different development servers in each "sub-website".
This allows us to use Vite development server mode and its React plugin for the minimal development website.
Meanwhile, we can continue to use <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer">Docusaurus</a> and its internal Webpack configuration for the documentation website.
To enable hot reloading during development of both websites, rather than <code>tsc</code>, we run <code>tsc --watch</code> so that the <code>dist</code> directory of each sub-package is updated on every source code change.
To further improve performance, we enable TypeScript's "incremental" mode with <code>"incremental": true</code>.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="react-18">React 18<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#react-18" class="hash-link" aria-label="Direct link to React 18" title="Direct link to React 18">​</a></h3>
<p>To leverage the benefits of React 18, we use the TypeScript option <code>"jsx": "react-jsx"</code>, which also comes with the minor benefit that JSX code can be written <a href="https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html" target="_blank" rel="noopener noreferrer">without explicitly importing</a> React at the top of each file.
To use React 18 in the context of Docusaurus and Material UI dependencies, which do not yet specify compatibility above React 17 in their <code>peerDependencies</code>, we use PNPM's <a href="https://pnpm.io/package_json#pnpmpeerdependencyrules" target="_blank" rel="noopener noreferrer">peerDependencyRules</a> in the root of the repository to override them and ensure that we are only depending on React 18 (as two versions of React cannot be included in the same web page).</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="externalizing-react">Externalizing React<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#externalizing-react" class="hash-link" aria-label="Direct link to Externalizing React" title="Direct link to Externalizing React">​</a></h3>
<p>Because the Vitessce React component is intended to be consumed by a React application (or other React component library), we can assume the consumer is using their own copy of React.
In fact, including two copies of React in the same web application typically causes a website to <a href="https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react" target="_blank" rel="noopener noreferrer">crash</a>.
To make React an external dependency in the main development and production bundles, we can use the <code>rollupOptions</code> part of the Vite configuration:</p>
<div class="language-js codeBlockContainer_HF71 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OR8B"><pre tabindex="0" class="prism-code language-js codeBlock_fq9o thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_oXbW"><span class="token-line" style="color:#393A34"><span class="token spread operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token literal-property property" style="color:#36acaa">rollupOptions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">external</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react-dom'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">output</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">globals</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">react</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'React'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">'react-dom'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ReactDOM'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token spread operator" style="color:#393A34">...</span><br></span></code></pre></div></div>
<p>Then, we can use the React-less Vitessce bundle in different contexts: <a href="https://github.com/vitessce/vitessce/blob/4bd70c0/sites/html/src/umd.html" target="_blank" rel="noopener noreferrer">UMD</a>, <a href="https://github.com/vitessce/vitessce/blob/4bd70c0/sites/html/src/es.html" target="_blank" rel="noopener noreferrer">ESM with importmap</a>, or <a href="https://observablehq.com/@keller-mark/vitessce-from-unpkg" target="_blank" rel="noopener noreferrer">d3.require.alias</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="managing-and-publishing-sub-packages">Managing and publishing sub-packages<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#managing-and-publishing-sub-packages" class="hash-link" aria-label="Direct link to Managing and publishing sub-packages" title="Direct link to Managing and publishing sub-packages">​</a></h3>
<p>To streamline the management of many sub-packages, we use PNPM and its <a href="https://github.com/pnpm/meta-updater" target="_blank" rel="noopener noreferrer">meta-updater</a> plugin.
This allows syncing changes to every sub-package's <code>package.json</code> file, for instance to update the main <code>"version"</code> or particular dependency version values.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="glsl-shaders-and-glslify-shader-modules">GLSL shaders and glslify shader modules<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#glsl-shaders-and-glslify-shader-modules" class="hash-link" aria-label="Direct link to GLSL shaders and glslify shader modules" title="Direct link to GLSL shaders and glslify shader modules">​</a></h3>
<p>To implement custom DeckGL shaders and extensions, we need to write GLSL shader code.
We use the <a href="https://github.com/glslify/glslify" target="_blank" rel="noopener noreferrer">glslify</a> shader module system to leverage published GLSL functions such as <a href="https://github.com/glslify/glsl-colormap" target="_blank" rel="noopener noreferrer">colormaps</a>.
Writing GLSL code in <code>.glsl</code> files enables IDE support but introduces the need for a build step when they are imported into JS files.
The glslify module system also requires a build step to substitute strings like <code>#pragma glslify: plasma = require("glsl-colormap/plasma")</code> with the actual colormap function.
As a workaround to prevent a build step, we can use the glslify CLI and simply include the shaders as strings in our JS files.
This is not ideal, so we will need to look into ways to address the IDE support and modularity that were lost.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="style-definitions">Style definitions<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#style-definitions" class="hash-link" aria-label="Direct link to Style definitions" title="Direct link to Style definitions">​</a></h3>
<p>To style our React components, we previously used a combination of SCSS and CSS-in-JS (via Material UI v4 + JSS).
SCSS requires a build step to convert the source code to plain CSS code.
Further, CSS imports in general also require a build step as they must be transformed from the JS-like import syntax to JS-compatible syntax.
To address this, we have manually converted all SCSS to JSS using Material UI's <a href="https://v4.mui.com/styles/basics/#hook-api" target="_blank" rel="noopener noreferrer">makeStyles</a> function and theming utilities.
(Note: we have not upgraded to Material UI v5 which <a href="https://mui.com/material-ui/migration/migrating-from-jss/" target="_blank" rel="noopener noreferrer">no longer uses JSS</a> so this solution may not be advised in a new project.)
Using JSS everywhere comes with the benefits of standardizing our styling code and facilitating direct style injection for consumers (i.e., no need for library users to include or think about a separate CSS file).
While JSS auto-generates class names by default (which prevents naming conflicts), the <a href="https://cssinjs.org/jss-plugin-global" target="_blank" rel="noopener noreferrer">jss-global-plugin</a> enables global class names to be used to refer to elements defined by dependencies such as <a href="https://www.npmjs.com/package/react-grid-layout" target="_blank" rel="noopener noreferrer">react-grid-layout</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="testing">Testing<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#testing" class="hash-link" aria-label="Direct link to Testing" title="Direct link to Testing">​</a></h3>
<p>To modernize our testing infrastructure, we have adopted Jest + Vitest + React Testing Library and upgraded to the latest version of Cypress.
However, there were a few hiccups, namely using code that depends on Web Workers in unit tests and serving test fixtures as static files during unit tests.
Jest + Vitest use jsdom rather than a real web browser during unit tests, for efficiency.
However, jsdom lacks support for web workers, which can be addressed by including the <a href="https://www.npmjs.com/package/jsdom-worker" target="_blank" rel="noopener noreferrer">jsdom-worker</a> library in the test setup script.
To serve static files during unit testing, we can hook into Vite's development server and add static file routes via <a href="https://github.com/expressjs/serve-static" target="_blank" rel="noopener noreferrer">serve-static</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="bundle-analysis">Bundle analysis<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#bundle-analysis" class="hash-link" aria-label="Direct link to Bundle analysis" title="Direct link to Bundle analysis">​</a></h3>
<p>It can be difficult to know what matters when it comes to the minified bundle size, and where to focus efforts when trying to reduce the size.
Visualizing the bundle composition is a great way to understand which dependencies or source files are contributing.
Luckily, <a href="https://www.npmjs.com/package/rollup-plugin-visualizer" target="_blank" rel="noopener noreferrer">rollup-plugin-visualizer</a> is compatible with this build setup because Vite uses the <a href="https://vitejs.dev/guide/using-plugins.html" target="_blank" rel="noopener noreferrer">rollup plugin API</a>.
From this visual analysis, I found that three different versions of the Zarr package were being included in the bundle, which I resolved using PNPM's <a href="https://pnpm.io/package_json#pnpmoverrides" target="_blank" rel="noopener noreferrer">overrides</a> in the root of the monorepo.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="concluding-thoughts">Concluding thoughts<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/internal-refactoring/#concluding-thoughts" class="hash-link" aria-label="Direct link to Concluding thoughts" title="Direct link to Concluding thoughts">​</a></h2>
<p>Hopefully, most JavaScript developers only have a small subset of these requirements, but maintainers of more complex projects might find these notes useful.
If you are interested in learning more, check out the Vitessce <a href="https://github.com/vitessce/vitessce" target="_blank" rel="noopener noreferrer">repository</a> or a <a href="https://github.com/keller-mark/pnpm-ts-react-example" target="_blank" rel="noopener noreferrer">minimal example</a>.</p>]]></content:encoded>
            <category>internals</category>
        </item>
        <item>
            <title><![CDATA[Observation-by-feature support has arrived]]></title>
            <link>http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/</link>
            <guid>http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/</guid>
            <pubDate>Sat, 16 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Since the start of the Vitessce project in late 2018, our team has been focused on addressing the challenges of interactive web-based visualization of multi-modal and spatial single-cell datasets.]]></description>
            <content:encoded><![CDATA[<p>Since the start of the Vitessce project in late 2018, our team has been focused on addressing the challenges of interactive web-based visualization of <strong>multi-modal</strong> and <strong>spatial</strong> single-cell datasets.</p>
<p>However, this problem has a broad scope and we've had to prioritize certain features over others: we have <a href="https://github.com/vitessce/vitessce/issues?q=is%3Aissue+is%3Aopen+label%3Afeature" target="_blank" rel="noopener noreferrer">142 open feature requests</a>.
This broad scope, coupled with the initial set of technologies used to generate data in the first phase of the HuBMAP Consortium (the first user of Vitessce), meant that we initially developed many features for scRNA-seq and bioimaging datasets.
Early on, we also discovered a gap in web-based tools for dynamic rendering of bioimaging data on the client side, so members of our team took a detour to develop the <a href="https://doi.org/10.1038/s41592-022-01482-7" target="_blank" rel="noopener noreferrer">Viv</a> library.
Meanwhile, the HuBMAP Consortium moved into its second phase, adding new labs and contributors, and generating a wide range of multi-modal datasets using technologies like SNARE-seq.</p>
<p>In parallel, the single-cell community is generalizing the cell-by-gene matrix from transcriptomics to represent other types of experimental data using "observation-by-feature" matrices (abbreviated <strong>FOM</strong> in the <a href="https://github.com/single-cell-data/mams/" target="_blank" rel="noopener noreferrer">Feature and Observation Matrix Schema Working Group</a> terminology).
This is enabling an <a href="https://scverse.org/" target="_blank" rel="noopener noreferrer">ecosystem</a> of tools to be developed around the data structure, such as <a href="https://anndata.readthedocs.io/" target="_blank" rel="noopener noreferrer">AnnData</a> and <a href="https://scanpy.readthedocs.io/" target="_blank" rel="noopener noreferrer">Scanpy</a>.
Emerging formats such as <a href="https://mudata.readthedocs.io/en/latest/" target="_blank" rel="noopener noreferrer">MuData</a> extend this paradigm yet further to support multi-modal use cases via container data structures that hold <em>multiple</em> observation-by-feature matrices.
Multiple observation-by-feature matrices may share the feature axis (multi-batch), the observation axis (multi-modal), neither axis (diagonal), or some combination (mosaic).
Our tools should support visualization of datasets that conform to any of these four cases.</p>
<p><img decoding="async" loading="lazy" alt="Figure 1, Argelaguet et al., Nature Biotechnology 2021" src="http://vitessce.io/docs/2025-08-05/30b1ac11/assets/images/fig1_argelaguet_2021-c7172cecfaace11beb6d02c81503cbcc.png" width="2408" height="1392" class="img_MiZr"></p>
<p style="text-align:right;width:100%">Adapted from Figure 1, Argelaguet et al., Nature Biotechnology 2021</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="updates-in-vitessce">Updates in Vitessce<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#updates-in-vitessce" class="hash-link" aria-label="Direct link to Updates in Vitessce" title="Direct link to Updates in Vitessce">​</a></h2>
<p>Vitessce was initally coupled to the more specific cell-by-gene ideas, preventing straightforward use with non-transcriptomics or multi-modal datasets.
We needed a major update to realize the FOM paradigm in Vitessce.
In our latest update, we have changed how we represent data and how we configure mappings between data and views in order to support the visualization of datasets containing multiple observation-by-feature matrices.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="data-types">Data types<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#data-types" class="hash-link" aria-label="Direct link to Data types" title="Direct link to Data types">​</a></h3>
<p><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/data-types-file-types/">Data types</a> no longer assume observation types like <code>cell</code>.</p>
<p>Instead, to facilitate re-use across observation types, we have split up each of the former data types into multiple more minimal data types.
For example, <code>obsLocations</code> stores (x, y) coordinates per observation, <code>obsEmbedding</code> stores (dim1, dim2) coordinates per observation for a given embedding type, and <code>obsSegmentations</code> stores polygon vertices per observation.
Simplified data types should also make it easier to implement new file types (including via plugins).</p>
<p>The former <code>raster</code> data type has been split into two new data types, <code>image</code> and <code>obsSegmentations</code>, to better reflect semantics rather than exposing the implementation differences between bitmask and polygon segmentations. (Internally, the fact that an <code>obsSegmentations</code> file type may return either polygons or bitmasks is handled by returning an <code>obsSegmentationsType</code> from the data loader class.)</p>
<p>For the full list of data types, please visit the updated <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/data-types-file-types/">Data Types and File Types</a> page.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="dataset-definitions">Dataset definitions<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#dataset-definitions" class="hash-link" aria-label="Direct link to Dataset definitions" title="Direct link to Dataset definitions">​</a></h3>
<p>Each of the <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-json/#datasets"><code>datasets</code></a> in the view configuration may now contain multiple file definitions corresponding to the same data type.
Previously, dataset definitions were limited to one file per data type.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="new-coordination-types">New coordination types<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#new-coordination-types" class="hash-link" aria-label="Direct link to New coordination types" title="Direct link to New coordination types">​</a></h3>
<p>The new coordination types <code>obsType</code>, <code>featureType</code>, and <code>featureValueType</code> enable "entity types" to be specified.</p>
<p>For example, if we have a cell-by-peak matrix from an scATAC-seq experiment, then the entities along the observation axis are cells and the entities along the feature axis are peaks:</p>
<div class="language-js codeBlockContainer_HF71 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OR8B"><pre tabindex="0" class="prism-code language-js codeBlock_fq9o thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_oXbW"><span class="token-line" style="color:#393A34"><span class="token literal-property property" style="color:#36acaa">dataset</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'my_atac_experiment'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token literal-property property" style="color:#36acaa">obsType</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'cell'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token literal-property property" style="color:#36acaa">featureType</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'peak'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token literal-property property" style="color:#36acaa">featureValueType</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'count'</span><br></span></code></pre></div></div>
<p>With these coordination types:</p>
<ul>
<li>more accurate strings like <code>100 cells x 200 peaks</code> can be rendered in the interface (as opposed to the more abstract <code>100 observations x 200 features</code>).</li>
<li>specific values like <code>featureType: 'gene'</code> can be used to offer specific analysis functionality (e.g., differential expression tests that are valid only for transcriptomics experiments).</li>
<li>the mapping between files and views can leverage both data types and entity types, as described in the next section.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="mapping-between-files-and-views">Mapping between files and views<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#mapping-between-files-and-views" class="hash-link" aria-label="Direct link to Mapping between files and views" title="Direct link to Mapping between files and views">​</a></h3>
<p>File lookup from within a view now takes into account<br> <code>(dataset, dataType, viewCoordinationValues)</code>.</p>
<p>The <code>viewCoordinationValues</code> refers to the coordination values that a particular view takes on at a given point in time.
This is required to disambiguate between multiple files within a dataset which have the same <code>dataType</code> (now that each dataset may contain multiple files per data type).</p>
<p>The diagram below shows how the mapping between views and files would be performed for a configuration containing one dataset and three views.</p>
<div class="viewConfigTabs_r3BD"><div class="tabs-container tabList_jcOf"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_i_Ab tabs__item--active">Heatmap</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_i_Ab">Scatterplot</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_i_Ab">Spatial</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_i_Ab">Hide matches</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_HKJQ"><img src="http://vitessce.io/docs/2025-08-05/30b1ac11/img/matching/heatmap.png" alt="heatmap" title="heatmap"></div><div role="tabpanel" class="tabItem_HKJQ" hidden=""><img src="http://vitessce.io/docs/2025-08-05/30b1ac11/img/matching/scatterplot.png" alt="scatterplot" title="scatterplot"></div><div role="tabpanel" class="tabItem_HKJQ" hidden=""><img src="http://vitessce.io/docs/2025-08-05/30b1ac11/img/matching/spatial.png" alt="spatial" title="spatial"></div><div role="tabpanel" class="tabItem_HKJQ" hidden=""><img src="http://vitessce.io/docs/2025-08-05/30b1ac11/img/matching/none.png" alt="none" title="none"></div></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="joint-file-types">Joint file types<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#joint-file-types" class="hash-link" aria-label="Direct link to Joint file types" title="Direct link to Joint file types">​</a></h3>
<p><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/data-types-file-types/#joint-file-types">Joint file types</a> can now be defined via functions which expand one file definition into an array of file definitions.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="name-changes">Name changes<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#name-changes" class="hash-link" aria-label="Direct link to Name changes" title="Direct link to Name changes">​</a></h3>
<p><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/components/">View type</a> names have been updated to use <code>obs</code> and <code>feature</code> terminology (e.g., <code>cellSets</code> is now <code>obsSets</code> and <code>genes</code> is now <code>featureList</code>).</p>
<p><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/coordination-types/">Coordination type</a> names have been updated to use <code>obs</code> and <code>feature</code> terminology (e.g., <code>cellColorEncoding</code> is now <code>obsColorEncoding</code> and <code>geneExpressionColormap</code> is now <code>featureValueColormap</code>).</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="exported-constants">Exported constants<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#exported-constants" class="hash-link" aria-label="Direct link to Exported constants" title="Direct link to Exported constants">​</a></h3>
<p>Exported <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/constants/">constants</a> have been updated to reflect the changes to view types, file types, data types, and coordination types as described here.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="file-definitions">File definitions<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#file-definitions" class="hash-link" aria-label="Direct link to File definitions" title="Direct link to File definitions">​</a></h3>
<p><a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-json/#files">File definitions</a> no longer require the <code>type</code> (data type) property.
Instead, we now <a href="https://github.com/vitessce/vitessce/blob/main/src/app/constant-relationships.js" target="_blank" rel="noopener noreferrer">map</a> file types to data types internally.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="view-config-schema">View config schema<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#view-config-schema" class="hash-link" aria-label="Direct link to View config schema" title="Direct link to View config schema">​</a></h3>
<p>The changes described in this blog post are reflected in the view config schema <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-json/#version">versions</a> <code>1.0.15</code> and above.
You can <a href="https://observablehq.com/@keller-mark/vitessce-config-version-diff?ref=main&amp;prev=1.0.10&amp;next=1.0.15" target="_blank" rel="noopener noreferrer">diff</a> new and old versions to see the changes.</p>
<h3 class="anchor anchorWithStickyNavbar_IeTi" id="vitessceconfig-constructor">VitessceConfig constructor<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#vitessceconfig-constructor" class="hash-link" aria-label="Direct link to VitessceConfig constructor" title="Direct link to VitessceConfig constructor">​</a></h3>
<p>The <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-js/">VitessceConfig</a> constructor now requires <code>schemaVersion</code> as a named parameter.</p>
<p>This will help us to understand which schema version the config was constructed against, enabling better warning messages and error handling.
For instance, the API may show warnings or throw errors when passed invalid constants based on the specified view config schema version.
New coordination types like <code>obsType</code> would be invalid when using an older schema version such as <code>1.0.7</code>.
We intend to update the constructors in R and Python to enforce this requirement as well.</p>
<p>For new code, we recommend specifying the latest view config schema version:</p>
<div class="language-js codeBlockContainer_HF71 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OR8B"><pre tabindex="0" class="prism-code language-js codeBlock_fq9o thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_oXbW"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> vc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">VitessceConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">schemaVersion</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1.0.15"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"My config"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>For a full list of valid <code>schemaVersion</code> values, see the table of <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/view-config-json/#version">schema versions</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="next-steps">Next steps<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#next-steps" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps">​</a></h2>
<p>We hope that these changes make it easier to configure, <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/data-file-types/">prepare data</a>, and <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/dev-plugins/">develop plugins</a> for visualizing one or more related single-cell experiments with Vitessce.
While we want our tools to be general enough to facilitate many use cases, we recognize the danger of over abstraction (e.g., preventing users from easily configuring Vitessce), and hope that our implementation strikes a useful balance.
We welcome your feedback via <a href="https://github.com/vitessce/vitessce/issues" target="_blank" rel="noopener noreferrer">issues</a> and <a href="https://github.com/vitessce/vitessce/discussions" target="_blank" rel="noopener noreferrer">discussions</a>.</p>
<p>Some additional use cases that we are excited about:</p>
<ul>
<li>using <code>obsType: 'nucleus'</code> to accurately refer to observations from <a href="https://doi.org/10.1038/nmeth.4407" target="_blank" rel="noopener noreferrer">single-nucleus</a> experiments (similarly <code>obsType: 'nucleolus'</code>, <code>obsType: 'mitochondria'</code>, etc. as technologies for <a href="https://doi.org/10.1002/smll.202005793" target="_blank" rel="noopener noreferrer">isolating subcellular components</a> mature)</li>
<li>using <code>featureType: 'isoform'</code> to accurately refer to features from <a href="https://doi.org/10.1038/s41587-022-01231-3" target="_blank" rel="noopener noreferrer">isoform-resolved</a> sequencing- and FISH-based experiments</li>
<li>using <code>featureType: 'topic'</code> and <code>featureValueType: 'probability'</code> to enable visualization of <a href="https://doi.org/10.1038/s41592-019-0367-1" target="_blank" rel="noopener noreferrer">topic model-based</a> analysis results</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="demos">Demos<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#demos" class="hash-link" aria-label="Direct link to Demos" title="Direct link to Demos">​</a></h2>
<p>The following examples showcase how the updates described here can be used to perform integrative visualization of single-cell data.</p>
<!-- -->
<h2 class="anchor anchorWithStickyNavbar_IeTi" id="migration">Migration<a href="http://vitessce.io/docs/2025-08-05/30b1ac11/blog/obs-by-feature-update/#migration" class="hash-link" aria-label="Direct link to Migration" title="Direct link to Migration">​</a></h2>
<p>The Vitessce JavaScript, Python, R packages will continue to be backwards compatible with previous view config schema versions
(i.e., using <code>version: '1.0.1'</code> in a JSON view config would result in the previously documented behavior despite upgrading the Vitessce package version).</p>
<p>For developers looking to upgrade to the latest version of the <code>vitessce</code> JavaScript package, please visit the <a href="http://vitessce.io/docs/2025-08-05/30b1ac11/docs/upgrade-guide/">upgrade guide</a>.</p>]]></content:encoded>
            <category>fom</category>
        </item>
    </channel>
</rss>