Public
Like
MolstarViewer
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data β all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v461View latest version
A collection of Molstar molecular visualization applications built for Val Town, using JSR imports and TSX.
This Val serves four different Molstar visualization applications:
- Viewer 𧬠- Standard Mol* viewer for visualizing molecular structures
- Docking Viewer π¬ - Specialized viewer for molecular docking results
- Mesoscale Explorer π - Explore mesoscale biological structures
- MVS Stories π - Interactive molecular visualization stories
Following the Val Town blog pattern with routes at the top level:
MolstarViewer/
βββ index.tsx # Main Hono app (entry point)
βββ routes/ # Route handlers (return HTML directly)
β βββ home.tsx # Homepage listing all apps
β βββ viewer.tsx # Viewer route (returns HTML string)
β βββ viewer/ # Viewer-specific modules (if needed)
β βββ docking-viewer.tsx # Docking viewer route
β βββ docking-viewer/ # Docking-specific modules (if needed)
β βββ mesoscale-explorer.tsx # Mesoscale route
β βββ mesoscale-explorer/ # Mesoscale-specific modules (if needed)
β βββ mvs-stories.tsx # MVS stories route
β βββ styles/ # CSS files
β βββ molstar.css # Compiled Molstar styles
βββ shared/ # Shared types and utilities
β βββ types.ts
β βββ README.md
βββ utils/ # Utility functions
βββ README.md # This file
- Runtime: Val Town (Deno-based)
- Framework: Hono v4 from JSR
- UI: React 18.2.0 via esm.sh
- Molstar: JSR package
jsr:@zachcp/molstar@5.3.17 - Language: TypeScript/TSX
All dependencies are imported using the JSR protocol:
import { Viewer } from "jsr:@zachcp/molstar@5.3.17";
Available exports from jsr:@zachcp/molstar:
.(main export - includes Viewer class)./plugin-ui./plugin./plugin-state./canvas3d./data./state./task./util./extensions
import { Hono } from "jsr:@hono/hono@4";
JSR (JavaScript Registry) provides native Deno/TypeScript support and works seamlessly in Val Town's runtime environment.
The top-level index.tsx is the main Hono app that imports and mounts all routes:
/** @jsxImportSource https://esm.sh/react@18.2.0 */
import { Hono } from "jsr:@hono/hono@4";
import homeRoutes from "./routes/home.tsx";
import viewerRoute from "./routes/viewer.tsx";
// ... other routes
const app = new Hono();
app.onError((err, c) => {
throw err; // Unwrap errors for debugging
});
app.route("/", homeRoutes);
app.route("/", viewerRoute);
// ... mount other routes
export default app.fetch;
Each route is a separate Hono app that returns HTML directly using template literals:
/** @jsxImportSource https://esm.sh/react@18.2.0 */
import { Hono } from "jsr:@hono/hono@4";
import { readFile } from "https://esm.town/v/std/utils@85-main/index.ts";
const app = new Hono();
app.get("/viewer", async (c) => {
const molstarCss = await readFile("/routes/styles/molstar.css", import.meta.url);
return c.html(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mol* Viewer</title>
<script src="https://esm.town/v/std/catch"></script>
<style>${molstarCss}</style>
</head>
<body>
<div id="molstar-container">Loading Mol* Viewer...</div>
<script type="module">
import { Viewer } from "jsr:@zachcp/molstar@5.3.17";
window.addEventListener("DOMContentLoaded", async () => {
const container = document.getElementById("molstar-container");
if (container) {
const viewer = await Viewer.create(container, {
layoutIsExpanded: true,
layoutShowControls: true,
});
}
});
</script>
</body>
</html>
`);
});
export default app;
- Routes return HTML directly using JavaScript template literals
- No React server-side rendering needed - simpler and faster
- CSS is injected inline from compiled Molstar stylesheets
- Client-side Molstar initialization happens via inline module scripts
- JSR imports work perfectly in client-side
<script type="module">tags
The project is currently scaffolded with:
- β Top-level route structure (Val Town blog style)
- β
Main Hono app in
index.tsx - β Routes return HTML directly (no React SSR)
- β Separate route files for each app
- β Viewer app fully functional with JSR imports
- β Compiled Molstar CSS integration
- β
No separate
frontend/orbackend/folders - β³ Docking viewer (scaffolded)
- β³ Mesoscale explorer (scaffolded)
- β³ MVS Stories (scaffolded)
- Simple HTML Templates: Routes return HTML strings using template literals - no React SSR
- CSS Handling: Molstar CSS is read from
routes/styles/molstar.cssand injected inline - Client-Side Initialization: Molstar viewer is initialized in browser using inline
<script type="module"> - JSR Imports: Work perfectly in client-side
<script type="module">tags - Error Handling:
app.onErrorunwraps Hono errors to show full stack traces - Error Catching: Include
https://esm.town/v/std/catchto capture client-side errors
- Implement remaining apps:
- Docking Viewer: Load and compare docking results
- Mesoscale Explorer: Load mesoscale structures
- MVS Stories: Story navigation and playback
- Add interactive controls for each viewer
- Add loading states and error handling in UI
- Implement app-specific features:
- Viewer: Load PDB structures by ID
- Add URL parameters for structure loading
- Add preset views and controls
- Mol* Website
- Val Town Documentation
- JSR Package Registry
- Hono Framework
- Val Town Blog Example - Structure inspiration
- Routes use template literals to return HTML strings directly (no React)
- JSR imports use the
jsr:protocol (e.g.,jsr:@zachcp/molstar@5.3.17) - Routes are separate Hono apps that get mounted in main
index.tsx - All route logic lives in the
routes/folder - Client-side scripts use
<script type="module">for JSR imports - Molstar CSS is loaded server-side and injected inline via template literals
- No
backend/orfrontend/folders - everything colocated inroutes/ - Main entry point is
index.tsxat project root - Routes and components live together in
routes/folder - Use
readFileutility for reading project files - JSR imports work seamlessly in Val Town's Deno runtime
When adding new features:
- Create a new route file in
routes/(e.g.,my-feature.tsx) - Route should return HTML using template literals with
c.html() - Include
<script type="module">for client-side initialization - Mount the route in
index.tsxwithapp.route("/", myFeatureRoute) - Keep shared types in
shared/types.ts - Use JSR imports for Molstar dependencies in client-side scripts
- Follow Val Town best practices from
AGENTS.md
Copyright (c) 2018-2025 mol* contributors, licensed under MIT