I used Google AI Studio for the first time to modify the webpage code made during Gemini creates a map browser
Pre-requisites:
Google Account
Web Server
Recommended:
Google Drive
WSL2 for a local web server
Conclusion: Free OpenAI.com is better than Gemini-3.5-Flash but lets you upload more files
Start by browsing to https://aistudio.google.com/
Accept the terms of service; do not accept the marketing emails.
You start at the "Playground".
Click the "+" icon in the input line.
Select multiple files and click Open; I used the web-page files from my WSL2 folder.
Click "Acknowledge" that you own the rights to what you upload, and that it can be used to "improve Google products".
At the top next to Playground, the number of tokens will be displayed. Click to see the token limits; for me, the limit is 1048576, and the full "map_data.json" was too large (6 million tokens). The tokens used by each file are displayed at the top, and files can be removed.
If I knew better, I'd suggest "System instructions".
Select "Thinking level" High.
Enter the prompt and press Ctrl-Enter (or click Run Ctrl <-).
[Files were uploaded]
This is a webpage to display map data. Can you add a feature to index.html and/or map_data_render.js to zoom in and out using the mousewheel, and pan using right or middle mouse-drag?
(!) An internal error has occurred.
I realized the token size was too large. So I removed the full map data, and uploaded the map_data.schema.json
[map_data.schema.json]
I uploaded a full map, but it was too large. The map_data.schema.json tries to describe the elements inside of map_data.json.
This time it started "Thinking". I clicked "Thoughts" to "view model thoughts", this time it listed some ideas followed by code.
A popup appeared, asking me to "Save your conversations in Google Drive (Recommended)".
I clicked "Enable Google Drive", then chose the account.
To enable zooming with the mousewheel and panning with right or middle mouse-drag, we can introduce view state variables (zoom, offsetX, offsetY) to the canvas coordinate engine.
Below is the updated map_data_render.js implementation. It applies standard transform matrices to the 2D rendering context, translates the coordinate math for cell/burg click detection, and implements mouse drag and wheel event listeners on the canvas.
Updated map_data_render.js
For the output file, clicking the download icon saves it as "ai_studio_code.js"; it's easier to click "Copy" and then paste over "map_data_render.js" in the test environment.
Finally, I loaded the web-page in my test environment and pressed Ctrl-F5.
Pan and zoom worked well, though with a minor issue of left-click not selecting things after you pan, until you right-click once.
I'd like to add several more changes.
When capital = 0, do not print the burg name on the map.
When selecting a State from the State tab, move the camera view to the cell indicated by "center".
When selecting a Burg from the Burgs tab, move the camera view to the cell indicated by "cell".
When a non-capital burg is clicked on the map, do print the burg name on the map.
Decrease the circle radius for burgs to 1/4 of before, and fonts on the map to half the size.
Double-clicking should re-center the view, with a 1-second pan.
There is a bug: after panning, I have to right-click once before I can left-click to select things.
(Thought for 42s):
To implement these additions and fix the dragging state bug, we can update the state tracking, camera centering animations, and draw logic in the JavaScript file.
Summary of Changes
Capital-Based Naming: Updated the rendering logic in drawMap() to only display burg names if they are capitals (capital !== 0) or if they are currently selected/clicked.
Camera Focus on Sidebar Select: Selecting a state centers the camera on its center cell index. Selecting a burg centers on its associated cell index.
Scaled Radius & Fonts: Reduced burg radius to 1/4 of before (capital ? 1.75 : 1.0) and reduced font size on the map to half of before (capital ? 5.5px : 5px).
Double-Click Re-centering: Added a double-click listener on the canvas that shifts the view smoothly over a 1-second transition using an cubic easing function (easeInOutCubic).
Drag State Reset Bug: Resolved the right-click drag state bug by properly resetting the dragging flags on mouseup.
Below is the complete, revised map_data_render.js file:
Again, it was very good; all of the changes I asked for were implemented. There were a few subtle bugs, so I asked to fix those.
Currently it seems to always zoom towards the mouse pointer. But if I click on a burg and it pans, it tends to zoom far off to the left of the screen. If it hasn't tracked the mouse pointer, zoom to center of screen by default.
Change the dragging to work for left-click instead of right-click; middle-button is still good for "pan-only".
Clicking on a cell updates "Selected inspect", but it might be showing the wrong cell information. For example, if I click on a burg of Blandge, it's cell is 2621, but if I click the cell that contains it, I see information for cell 4250. And many times I will click on a "water" cell and biome type will be "Cold Desert".
Blange In pack.burg:
{ "cell": 2621, "x": 321.33, "y": 505.19, "i": 40, "state": 17, "culture": 9, "name": "Blandge", "feature": 4, "capital": 0, "port": 1, "population": 0.499, "type": "Naval", "coa": { "t1": "gules", "division": { "division": "perFess", "t": "azure", "line": "potentySinister" }, "ordinaries": [{ "ordinary": "pilesInPoint", "t": "or", "divided": "counter" } ], "shield": "fantasy4" },
Index 2621 in pack.cell:
{ "i": 2621, "v": [4301, 4557, 4559, 4590, 4474], "c": [2623, 2712, 2710, 2622, 2620], "p": [318, 508.8], "g": 6352, "h": 20, "area": 40, "f": 4, "t": 1, "haven": 2623, "harbor": 1, "fl": 0, "r": 0, "conf": 0, "biome": 2, "s": 8, "pop": 2.6182644367218018, "culture": 9, "burg": 40, "routes": { "2620": 289, "2623": 488, "2712": 289 }, "state": 17, "religion": 9, "province": 169 }
(This time it had to do more thinking 20% of the way through the file).
Updated Implementation
The following updates address the zoom alignment, left-click drag and pan integration, and accurate spatial index query lookups:
In-Context Zoom Focus: Added canvas hover state trackers (mouseenter, mouseleave, and mousemove). If the mouse pointer is not currently on the canvas viewport, mousewheel zooms default cleanly to the exact geometric center of the canvas.
Left-Click Drags & Middle-Click Pan: Panning interactions are mapped to the left mouse button (button 0) and middle mouse button (button 1). On mouseup, a click action is triggered only if the mouse was not dragged beyond a threshold, separating selection from panning.
Accurate Spatial Query Lookups: Modified cell detection to calculate Euclidean distance to each cell's coordinate vector cell.p ([x, y]) centerpoint directly inside pack.cells. This resolves the coordinate mismatch bug that caused water cells to display incorrect biomes like "Cold Desert."
Inspected Non-Capital Names: Handled conditionally printing labels for selected non-capital burgs on the map.
Adjusted Visuals: Reduced settlement circle scale factor (burgRadius) to 1/4 of before and decreased map text sizes to 50%.
Here is the complete updated code for map_data_render.js:
It's uh interesting that it mentioned items 4 and 5 again, which had already been taken care of.
#1 isn't working, #2 and #3 are all good! And it didn't mess anything up for #4 or #5.
Let's ask for more.
After a burg is selected, clicking on it again should unselect it and select a instead.
Zooming after an "auto-pan" is still going to the wrong place. Before fixing that, try to guess what is going wrong.
In the Burgs list, use yellow font for capitols instead of white.
(This time it output the code without stopping)
The Zoom Glitch Diagnosis (Guess)
During an active "auto-pan" transition (e.g., when clicking a State or Burg from the sidebar lists), the camera offsets (offsetX and offsetY) are updated frame-by-frame via requestAnimationFrame using values captured before the animation began.
If you trigger a manual zoom (scroll wheel) during or immediately after this active animation period, the zoom calculation modifies the scale factor (zoom) and calculates new coordinates relative to the cursor. However, the active animation frame loop continues executing in the background, repeatedly overwriting your new manual offsets with stale keyframe values. Because the scale factor changes but the offsets are locked to the animation curve, a coordinate mismatch is produced, shifting the camera unexpectedly (e.g., far off to the left).
We can resolve this issue by calling cancelActivePan() to stop any active animation loops instantly whenever a manual wheel scroll or dragging event begins.
Summary of New Changes
Inline Cursor Tracking for Zooming: Redesigned the pointer check inside the 'wheel' listener. By directly inspecting the wheel event's client coordinates, we no longer rely on external mouse tracking variables, ensuring center-focused zoom fallbacks are reliable when scrolling while hovering over sidebars.
Burg Double-Deselect Override: Clicking an already-selected burg deselects it and selects its associated map cell instead.
Sidebar Capitol Highlighting: Capital cities in the "Burgs" sidebar tab list now render in yellow (#ffcc00) instead of white.
Auto-Pan Cancellation: Integrated cancellation calls on mousedown and wheel events to instantly stop transition loops.
Here is the updated map_data_render.js file:
Yes, the zooming is fixed now. And I can cancel the auto-pan by clicking something else. Capitols in the burg list are yellow now, but I noticed that capitols are all at the start.
AI-studio seems to do things correctly much more than using Gemini in the browser. Perhaps the "thinking level" matters.
Now that clicking and zooming around are doing well, I'll ask for two more features and stop for the day after.
All of the features are working well now. Let's add a couple more:
Draw the pack.routes on the map. Use a different style depending on the "group"; the groups are "roads", "trails", and "searoutes". Trails can be not drawn unless zoomed in more, then skinny and a brown color. Roads can be a bit thicker and light-grey. Sea-routes should be thick but dashed with longer spacing between the dashes. Routes should be drawn above cells but behind burgs and other text.
Here's an example pack.roads element:
{ "i": 11, "group": "roads", "feature": 4, "points": [[679.99, 298.73, 1351], [675.06, 296.28, 1350], [670.8, 290.5, 1257], [672.71, 285.09, 1256] }
Next feature could be harder. I want to show the state borders. Possible algorithm: Draw the states borders by looking at each pack.cells[i] , and compare pack.cells[i].state to each pack.cells[pack.cells[i].c[j]].state , and drawing a thicker line between the cells when the states are different.
Updated Implementation
The following updates implement route drawing with group-specific styling, along with high-performance state border precomputation and rendering:
State Borders Precomputation: Introduced precomputeBorders(), which runs once during initialization to avoid doing expensive neighbor calculations on every draw frame. By finding shared vertices of adjacent cells with different state values, it precisely maps boundaries (including coastlines).
Layered Routing Engine: Drawn in the intermediate phase—above biomes and rivers, but underneath settlement markers and text labels.
Route Group Styling:
Trails: Hidden unless zoomed in (zoom >= 1.5), rendered in a thin brown color (#8B5A2B).
Roads: Always visible, rendered as light-grey lines (#cccccc) of moderate thickness.
Sea-routes: Rendered as thick blue-ish lines with long-dashed spacing (setLineDash([10, 8])).
Here is the updated map_data_render.js file:
Again, this was good. The lines were drawn how and where I wanted, and only needed a bit of adjustment.
What other "dashed line" styles could I use for sea-routes?
1. The Morse Code Style (Dot-Dash)
An alternating pattern of a long segment followed by a small dot. This is a common style for boundaries or maritime shipping tracks on historical maps.
codeJavaScript
ctx.strokeStyle = '#5a9bd4';
ctx.lineWidth = 1.6;
ctx.setLineDash([15, 6, 3, 6]); // [Long dash, space, short dot, space]
2. Rounded Dotted Line
By default, canvas dashes are rectangular blocks. If you change the line's end caps to round, a dash length of 0 creates circles. This results in a dotted path.
codeJavaScript
ctx.strokeStyle = '#5a9bd4';
ctx.lineWidth = 3.0;
ctx.lineCap = 'round'; // Rounds the edges of the dashes
ctx.setLineDash([0, 10]); // [0 length dash (circle), 10px spacing]
(and suggested 3 more styles, including an animated option)
Change the state borders to be half as thick, with rounded cap.
Change the sea routes to be the round dotted lines, and only be as wide as the roads.
Roads and trails look good.
Summary of Visual Adjustments
Refined State Borders: Decreased state border line-thickness from 1.4 to 0.7 and applied ctx.lineCap = 'round' to smooth out individual edge joins.
Dotted Sea Routes: Reconfigured the "searoutes" group styling to render as circular dotted lines (setLineDash([0, 6]) with a 'round' line-cap). The line-width matches the roads group at 1.0.
Canvas State Safety: Included proper lineCap resets at the end of route and border render blocks to prevent styles from bleeding into burg markers or highlight rings.
Here is the updated map_data_render.js file:
That looks almost exactly how I want it to now, great!
Giving the AI copies of the files was, as it would say, "a game-changer". I never fix the code, so I never needed to edit it, which of course means I understood it less than when I was compensating for the AI mistakes.
When I stopped after 3 hours (including breaks and pasting to this page), I had used "59 cents" worth of tokens, for free of course:
Token Usage:79923 / 1048576
Input tokens:16,948
Output tokens:62,975
Total tokens:79,923
Cost Estimation *
Input token cost:$0.025422
Output token cost:$0.566775
Total cost:$0.592197
Supposedly the chat is saved on Google Drive somewhere. Sharing the prompt would be more efficient than pasting here =)
The top line had a few options like "Make this an app", "Share prompt", "Compare mode", "Make a copy", "Raw mode", and "Delete".
"Make an app" had a line of options to scroll through, including "Build an Android app", "Add database and auth".
Since I already made a web server, with nothing valuable to lose in case of security flaws, I have put the updated code here:
https://axus.club/map_ai_studio/