Skip to content

Commit

Permalink
New UI + screenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
steinbro committed Jan 28, 2024
1 parent 3379ccb commit b8a356c
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 101 deletions.
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
# Soundscape Web Client

Experimenting with porting Soundscape functionality to JavaScript, to run in a Web browser.
All your favorite features of the Soundscape app for iOS, running in the browser of your choice.

## Live Demo
![Screenshot of Soundscape web client](screenshots/main_view.png)

### Places Near Me
Announce points of interest around your actual location.
Try it for yourself! https://soundscape-community.github.io/soundscape-web-client/

https://soundscape-community.github.io/soundscape-web-client/

To use a location other than what's reported by your device, include the latitude, longitude, and compass heading (degrees from north) in the URL, e.g. https://soundscape-community.github.io/soundscape-web-client/?lon=-77.006156&lat=38.897600&heading=0.0 will place you near Union Station in Washington, D.C.
## Tools for development and debugging

### Activity simulator

![Screenshot of GPX file replay view](screenshots/gpx_replay_view.png)

Select a local GPX file from your computer, and it will be replayed on a visual map with audio callouts.

https://soundscape-community.github.io/soundscape-web-client/replay_gpx.html

## Running locally
### Location override
To use a location other than what's reported by your device, include the latitude, longitude, and compass heading (degrees from north) in the URL, e.g. https://soundscape-community.github.io/soundscape-web-client/?lon=-77.006156&lat=38.897600&heading=0.0 will place you near Union Station in Washington, D.C.

## Running locally
1. Install the Python dependencies, and run the tile server proxy.
```
$ cd server
Expand Down
134 changes: 92 additions & 42 deletions app/css/main.css
Original file line number Diff line number Diff line change
@@ -1,69 +1,119 @@
html {
font-family: Arial, Helvetica, sans-serif;
/*
Copyright (c) Daniel W. Steinbrook.
with many thanks to ChatGPT
*/

body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
font-size: 1.2em; /* Increase default font size for better readability */
}

/* touch-friendly button */
button, select, label, input {
padding: 10px 20px;
font-size: 16px;
/* Top button bar */
nav {
display: flex;
justify-content: space-around;
background-color: #2c3e50;
padding: 15px;
}

.arrow-icon {
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 40px solid red; /* You can change the color */
transform-origin: bottom center;
nav button, nav input, nav select, button {
background-color: #e74c3c;
color: #fff;
border: none;
padding: 15px;
border-radius: 8px;
cursor: pointer;
font-size: 1.2em; /* Larger font size for buttons, inputs, and select */
margin-right: 10px; /* Add some spacing between controls */
max-width: 20%;
}

/* Speaking and playback rate selectors don't need to be as large */
nav input[type="number"] {
max-width: 10%;
}

/* Voice selector should be wider (names can be long) */
nav select {
max-width: 40%;
}

/* GPX file selector and seek position should be wider -- probably not running on a phone */
nav input[type="file"], nav input[type="range"] {
max-width: none;
}

main {
padding: 15px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}

#map {
height: 400px;
width: 100%;
height: 300px; /* Adjusted height for better visibility */
border: 2px solid #000; /* Higher contrast for the map border */
margin-bottom: 15px;
}

#recentCalloutsArea {
height: 400px;
height: calc(100vh - 435px); /* all vertical space after map + button row */
overflow-y: auto;
border: 1px solid #ccc;
}

/* display map + recent callouts side-by-side on sufficiently wide screens */
@media (min-width: 800px) {
#map {
width: 60%;
display: inline-block;
}
#recentCalloutsArea {
width: 38%;
display: inline-block;
}
flex-basis: 100%;
}

#recentCalloutsList {
list-style: none;
padding: 0;
margin: 0;
}

#recentCalloutsList li {
height: 20px;
#recentCalloutsList li, #recentCalloutsArea p {
list-style: none;
border-bottom: 2px solid #000; /* Higher contrast for list item borders */
padding: 15px;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 5px;
border-bottom: 1px solid #eee;
}

/* Active beacon controls */
#recentCalloutsArea p {
background-color: #e74c3c;
color: #fff;
font-weight: bold;
}

#recentCalloutsArea p button {
background-color: #2c3e50;
}

#recentCalloutsArea button {
padding: 0;
width: 60px;
}

#recentCalloutsArea p {
height: 20px;
margin: 0;
padding: 5px;
border-bottom: 1px solid #000;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
/* display map + recent callouts side-by-side on sufficiently wide screens */
@media screen and (min-width: 600px) {
#map {
height: calc(100vh - 140px); /* all vertical space after button row */
width: 48%;
}

#recentCalloutsArea {
height: calc(100vh - 140px); /* all vertical space after button row */
flex-basis: 48%;
}
}

/* Current position marker on map */
.arrow-icon {
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 40px solid red; /* You can change the color */
transform-origin: bottom center;
}
18 changes: 7 additions & 11 deletions app/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import { audioContext, createSpatialPlayer, playSpatialSpeech } from './audio/sound.js'
import createCalloutAnnouncer from './audio/callout.js'
import cache from './data/cache.js'
import { getLocation, watchLocation } from './spatial/geo.js';
import { startCompassListener } from './spatial/heading.js';
import createLocationProvider from './spatial/location.js'
Expand Down Expand Up @@ -77,16 +76,18 @@ document.addEventListener('DOMContentLoaded', function () {
var btnNearMe = document.getElementById('btn_near_me');
var watchPositionHandler = null;
var activeMode = null;
const selectedColor = '#18b3c3';
const unselectedColor = '#e74c3c';
// When mode button is clicked:
// If a mode is currently active, end that mode
// If mode button was different from current mode, start new mode
async function toggleMode(newMode) {
// required for iOS Safari: first speech must be directly triggered by user action
playSpatialSpeech(' ');

// Reset button labels
btnCallouts.textContent = 'Begin Tracking with Callouts';
btnNearMe.textContent = 'Announce Places Near Me';
// Reset button colors
btnCallouts.style.backgroundColor = unselectedColor;
btnNearMe.style.backgroundColor = unselectedColor;

// Clear queued audio
if (activeMode) {
Expand Down Expand Up @@ -120,11 +121,11 @@ document.addEventListener('DOMContentLoaded', function () {
startCompassListener(locationProvider.updateOrientation);

watchPositionHandler = watchLocation(locationProvider.updateLocation);
btnCallouts.textContent = 'End Tracking with Callouts';
btnCallouts.style.backgroundColor = selectedColor;
break;

case 'near_me':
btnNearMe.textContent = 'End Announce Places Near Me';
btnNearMe.style.backgroundColor = selectedColor;
getRelevantLocation().then(coords => {
console.log(coords);
locationProvider.updateLocation(coords.latitude, coords.longitude);
Expand Down Expand Up @@ -157,9 +158,4 @@ document.addEventListener('DOMContentLoaded', function () {
btnNearMe.addEventListener('click', function() {
toggleMode('near_me');
});

var btnClear = document.getElementById('btn_clear');
btnClear.addEventListener('click', function() {
cache.clear();
});
});
10 changes: 8 additions & 2 deletions app/js/replay.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { createSpatialPlayer } from './audio/sound.js'
import createCalloutAnnouncer from './audio/callout.js';
import cache from './data/cache.js'
import createLocationProvider from './spatial/location.js'
import replayGPX from './spatial/gpx.js';
import createMap from './visual/map.js'
Expand Down Expand Up @@ -85,13 +86,13 @@ document.addEventListener('DOMContentLoaded', function () {

// Toggle play/pause
if (!playing) {
playPauseButton.textContent = "Pause";
playPauseButton.textContent = "";
// Start triggering audio callouts
locationProvider.events.addEventListener('locationUpdated', announcer.locationChanged)
gpxPlayer.play();
playing = true;
} else {
playPauseButton.textContent = "Play";
playPauseButton.textContent = "";
// Strop triggering audio callouts
locationProvider.events.removeEventListener('locationUpdated', announcer.locationChanged);
audioQueue.stopAndClear();
Expand All @@ -109,4 +110,9 @@ document.addEventListener('DOMContentLoaded', function () {
gpxPlayer.seekTo(newIndex);
}
});

var btnClear = document.getElementById('btn_clear');
btnClear.addEventListener('click', function() {
cache.clear();
});
});
1 change: 1 addition & 0 deletions app/js/visual/recentlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function createRecentCalloutList(locationProvider, audioQueue) {
newCallout.innerHTML = ' ' + text;
const beaconLink = document.createElement('button');
beaconLink.innerHTML = '🔊';
beaconLink.title = 'Start beacon';
newCallout.insertBefore(beaconLink, newCallout.firstChild);

beaconLink.addEventListener('click', () => {
Expand Down
2 changes: 1 addition & 1 deletion app/js/visual/voicecontrols.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function createVoiceControls(audioQueue) {
audioQueue.voices.forEach(function(voice, index) {
const option = document.createElement('option');
option.value = index;
option.textContent = voice.name;
option.textContent = '🗣 ' + voice.name;
voiceSelect.appendChild(option);
});
}
Expand Down
34 changes: 12 additions & 22 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,18 @@
<script src="app/js/main.js" type="module"></script>
</head>
<body>
<p>
<button id="btn_callouts">Begin Tracking with Callouts</button>
<button id="btn_near_me">Announce Places Near Me</button>
</p>
<nav>
<button id="btn_callouts" title="Start tracking callouts"></button>
<button id="btn_near_me" title="Announce places near me"></button>
<select id="voice" name="voice" title="Voice" required></select>
<input type="number" id="rate" title="Speaking rate" value="2" name="rate" min="0.5" max="5" step="0.5">
</nav>

<p>
<label for="voice">Voice:</label>
<select id="voice" name="voice" required></select>
</p>

<p>
<label for="rate">Rate:</label>
<input type="number" id="rate" value="2" name="rate" min="0.5" max="5" step="0.5">
</p>

<div id="recentCalloutsArea">
<ul id="recentCalloutsList"></ul>
</div>
<div id="map"></div>

<p>
<button id="btn_clear">Clear Cached Data</button>
</p>
<main>
<div id="map"></div>
<section id="recentCalloutsArea">
<ul id="recentCalloutsList"></ul>
</section>
</main>
</body>
</html>
28 changes: 13 additions & 15 deletions replay_gpx.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,18 @@
<script src="app/js/replay.js" type="module"></script>
</head>
<body>
<p>
<label for="gpxFileInput">GPX file:</label>
<input type="file" name="gpxFileInput" id="gpxFileInput" />
</p>
<p>
<button id="playPauseButton">Play</button>
<label for="speed">Speedup factor:</label>
<input type="number" id="speed" name="speed" value="5" min="1" max="10" step="1">
<label for="pointSlider">Seek:</label>
<input type="range" name="pointSlider" id="pointSlider" min="0" max="100" value="0" />
</p>
<div id="recentCalloutsArea">
<ul id="recentCalloutsList"></ul>
</div>
<div id="map"></div>
<nav>
<input type="file" name="gpxFileInput" id="gpxFileInput" title="GPX file" />
<button id="playPauseButton" title="Play/pause"></button>
<input type="range" name="pointSlider" title="Seek position" id="pointSlider" min="0" max="100" value="0" />
<input type="number" id="speed" name="speed" title="Speed-up factor" value="5" min="1" max="10" step="1">
<button id="btn_clear" title="Clear cached data">&#128465;</button>
</nav>
<main>
<div id="map"></div>
<section id="recentCalloutsArea">
<ul id="recentCalloutsList"></ul>
</section>
</main>
</body>
</html>
Binary file added screenshots/gpx_replay_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/main_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b8a356c

Please sign in to comment.