add WebToys and BPS
BIN
src/assets/_root/webtoys/bps/bps.tar.gz
Normal file
BIN
src/assets/_root/webtoys/bps/bps.zip
Normal file
BIN
src/assets/_root/webtoys/bps/images/aliens_17_days.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
src/assets/_root/webtoys/bps/images/aliens_current_affairs.png
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
src/assets/_root/webtoys/bps/images/aliens_express_elevator.png
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
src/assets/_root/webtoys/bps/images/aliens_game_over.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
src/assets/_root/webtoys/bps/images/random.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
src/assets/_root/webtoys/bps/images/true_lies_navel_lint.png
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
src/assets/_root/webtoys/bps/images/true_lies_pathetic.png
Normal file
After Width: | Height: | Size: 91 KiB |
BIN
src/assets/_root/webtoys/bps/images/weird_science_booze.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
src/assets/_root/webtoys/bps/images/weird_science_dead_meat.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
src/assets/_root/webtoys/bps/images/weird_science_he_pukes.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
src/assets/_root/webtoys/bps/images/weird_science_sandwich.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
src/assets/_root/webtoys/bps/images/weird_science_stewwed.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
src/assets/_root/webtoys/bps/images/weird_science_turd_brain.png
Normal file
After Width: | Height: | Size: 86 KiB |
150
src/assets/_root/webtoys/bps/index.html
Normal file
@@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
/****************************************************************************
|
||||
* BPS (Bill Paxton Soundboard)
|
||||
*
|
||||
* Copyright 2024 Eric Woodward
|
||||
* Source released under CC0 Public Domain License v1.0
|
||||
* https://www.itsericwoodward.com/licenses/cc0/
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
****************************************************************************/
|
||||
-->
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>BPS (Bill Paxton Soundboard)</title>
|
||||
<link rel="stylesheet" href="styles/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="output"></div>
|
||||
|
||||
<!--
|
||||
`cat main.js | openssl dgst -sha256 -binary | openssl base64 -A`, then repeat for 384 and 512
|
||||
-->
|
||||
<script crossorigin="anonymous" integrity="sha256-YvGi/WY2dKJwaU3cCMLSkiJCnE2hFCiiUdRwqWTGvEE=
|
||||
sha384-mtemuzaWEW/0HsyxJyDCaqMquUejVMwzs5VSj5KOr0jrg0+bG/aV2JGsvn/5AbRP
|
||||
sha512-z5iEy8ijJzKBzzzBZSdt1DFqwsHLRAV2fvGk4N7P0VikiXtRla258zX7YiSvSIwYskOnnPySzFLaDAdXWUkgNQ=="
|
||||
src="scripts/main.js" type="module" ></script>
|
||||
|
||||
<noscript>
|
||||
<p>Sorry, but the Bill Paxton Soundboard requires JavaScript to work.</p>
|
||||
<p>Please enable it or try using a different browser.</p>
|
||||
</noscript>
|
||||
|
||||
<p class="center">
|
||||
<a class="downloadButton" href="bps.zip">Download ZIP</a>
|
||||
<a class="downloadButton" href="bps.tar.gz">Download TAR</a>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>What is This?</h2>
|
||||
|
||||
<p>
|
||||
This is the BPS (Bill Paxton Soundboard), my entry into the UI Developers Guild Coding Challenge for
|
||||
February 2024 at the company I work for.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you want to know a bit more about how (and why) it was made, be sure to check
|
||||
out <a href="/journal/2024/02-10-webtoys-bps">the blog post I wrote about it</a> when it was released
|
||||
on 2024-02-10.
|
||||
</p>
|
||||
|
||||
<h3>The Rules</h3>
|
||||
|
||||
<p>
|
||||
This particular challenge had 3 simple rules (copied verbatim below):
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
<li><em>Play some kind of music / sound</em></li>
|
||||
<li><em>Be viewable</em></li>
|
||||
<li><em>Don't work over 4hrs!!!!</em></li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
The BPS satisfies all 3 of the rules: it's an HTML5 / CSS / JS application that creates
|
||||
a series of virtual <code>audio</code> tags, and then loads a WAV or MP3 into each one before
|
||||
inserting them into the document. It then renders a series of clickable image-buttons
|
||||
(each one being an <code>img</code> tag, surrounded by a <code>figure</code> tag, and
|
||||
augmented by the text of a <code>figcaption</code> tag) and attaches a <code>play()</code>
|
||||
function to the click handlers for those <code>figure</code>s. Plus, I wrote it all in under 4 hours:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
One hour thinking through the concept and writing the rough draft
|
||||
<a href="scripts/player.js"><code>player.js</code></a> and
|
||||
<a href="scripts/main.js"><code>main.js</code></a> modules;
|
||||
</li>
|
||||
<li>
|
||||
One hour to turn draft into an MVP, addressing
|
||||
<a href="styles/styles.css">layout</a> and audio issues;
|
||||
</li>
|
||||
<li>
|
||||
One hour to add images and expand the audio selection; and
|
||||
</li>
|
||||
<li>
|
||||
One final hour to add mobile support and some light documentation.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>How to Install Locally</h3>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
Download either the <a href="bps.zip">ZIP'd</a>
|
||||
or <a href="bps.tar.gz">TAR-balled</a> version.
|
||||
</li>
|
||||
<li>
|
||||
Decompress it:
|
||||
<ul>
|
||||
<li>For the ZIP: <code>unzip bps.zip</code></li>
|
||||
<li>For the TAR-ball: <code>tar -xvzf bps.tar.gz</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Change to the new directory: <code>cd bps</code>
|
||||
</li>
|
||||
<li>
|
||||
Start an HTTP server: <code>npx http-server</code>
|
||||
</li>
|
||||
<li>
|
||||
Point your web browser to
|
||||
<a href="http://127.0.0.1:8080/">http://127.0.0.1:8080/</a>.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3>Copyright</h3>
|
||||
|
||||
<ul>
|
||||
<li><em>True Lies</em> is copyright 1994 20th Century Fox.</li>
|
||||
<li><em>Aliens</em> is copyright 1986 Twentieth Century Fox.</li>
|
||||
<li><em>Weird Science</em> is copyright 1985 Universal Pictures (I think).</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The images and sound clips used for the Bill Paxton Soundboard are copyright their
|
||||
respective owners, and used under the
|
||||
<a href="https://fairuse.stanford.edu/overview/fair-use/">fair use provision</a>
|
||||
of the <a href="https://constitution.congress.gov/constitution/">Constitution of the United
|
||||
States of America</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
All other content on this page (including scripts and text content) is released under a
|
||||
<a href="/licenses/cc0/">Creative Commons CC0 1.0 Universal</a>
|
||||
license.
|
||||
</p>
|
||||
|
||||
<p>Share and enjoy!</p>
|
||||
|
||||
<p class="center">
|
||||
<a href="/web.html">More of Eric's Web Stuff</a>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
138
src/assets/_root/webtoys/bps/scripts/main.js
Normal file
@@ -0,0 +1,138 @@
|
||||
// @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0-1.0
|
||||
/****************************************************************************
|
||||
* BPS (Bill Paxton Soundboard)
|
||||
*
|
||||
* Copyright 2024 Eric Woodward
|
||||
* Source released under CC0 Public Domain License v1.0
|
||||
* https://www.itsericwoodward.com/licenses/cc0/
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
****************************************************************************/
|
||||
|
||||
import { load, play } from "./player.js";
|
||||
import rootdir from "./rootdir.js";
|
||||
|
||||
export default (() => {
|
||||
// adapted from: https://cheatcode.co/tutorials/how-to-build-a-soundboard-with-javascript
|
||||
|
||||
const sounds = [
|
||||
{
|
||||
name: "true_lies_navel_lint",
|
||||
fmt: "wav",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Navel Lint",
|
||||
},
|
||||
{
|
||||
name: "true_lies_pathetic",
|
||||
fmt: "mp3",
|
||||
image: "aliens_17_days.png",
|
||||
text: "It's Pathetic!",
|
||||
},
|
||||
|
||||
{
|
||||
name: "aliens_game_over",
|
||||
fmt: "wav",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Game Over!",
|
||||
},
|
||||
{
|
||||
name: "aliens_current_affairs",
|
||||
fmt: "wav",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Current Affairs",
|
||||
},
|
||||
{
|
||||
name: "aliens_17_days",
|
||||
fmt: "wav",
|
||||
image: "aliens_17_days.png",
|
||||
text: "17 Days?",
|
||||
},
|
||||
{
|
||||
name: "aliens_express_elevator",
|
||||
fmt: "wav",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Express Elevator",
|
||||
},
|
||||
|
||||
{
|
||||
name: "weird_science_booze",
|
||||
fmt: "mp3",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Boozehounds Return",
|
||||
},
|
||||
{
|
||||
name: "weird_science_dead_meat",
|
||||
fmt: "mp3",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Dead Meat",
|
||||
},
|
||||
{
|
||||
name: "weird_science_sandwich",
|
||||
fmt: "mp3",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Pork Sandwich",
|
||||
},
|
||||
{
|
||||
name: "weird_science_stewwed",
|
||||
fmt: "mp3",
|
||||
image: "aliens_17_days.png",
|
||||
text: "You're Stewwed",
|
||||
},
|
||||
{
|
||||
name: "weird_science_turd_brain",
|
||||
fmt: "mp3",
|
||||
image: "aliens_17_days.png",
|
||||
text: "Turd Brain",
|
||||
},
|
||||
];
|
||||
|
||||
sounds.forEach(({ name, fmt }) => {
|
||||
load(name, `${rootdir}/sounds/${name}.${fmt}`);
|
||||
});
|
||||
|
||||
// adapted from https://stackoverflow.com/questions/12813573/position-icons-into-circle
|
||||
|
||||
let m = sounds.length; /* how many are ON the circle */
|
||||
let tan = Math.tan(Math.PI / m); /* tangent of half the base angle */
|
||||
|
||||
const build = () => {
|
||||
const figureButtons = sounds.map(({ name, text }, idx) => {
|
||||
const caption = document.createElement("figcaption"),
|
||||
figure = document.createElement("figure"),
|
||||
img = document.createElement("img");
|
||||
caption.textContent = text;
|
||||
img.alt = text;
|
||||
img.src = `${rootdir}/images/${name}.png`;
|
||||
figure.className = "figureButton";
|
||||
figure.onclick = () => play(name);
|
||||
figure.style = `--i: ${idx}`;
|
||||
figure.append(...[img, caption]);
|
||||
return figure;
|
||||
});
|
||||
|
||||
const randomFigure = document.createElement("figure"),
|
||||
randomCaption = document.createElement("figcaption"),
|
||||
randomImg = document.createElement("img");
|
||||
randomCaption.textContent = "Random";
|
||||
randomImg.alt = "Random";
|
||||
randomImg.src = `${rootdir}/images/random.png`;
|
||||
randomFigure.className = "figureButton";
|
||||
randomFigure.onclick = () =>
|
||||
play(sounds[~~(sounds.length * Math.random())].name);
|
||||
randomFigure.append(...[randomImg, randomCaption]);
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "circleWrapper";
|
||||
div.style = `--m: ${m}; --tan: ${+tan.toFixed(2)}`;
|
||||
div.append(...[randomFigure, ...figureButtons]);
|
||||
document.getElementById("output").replaceWith(div);
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
setTimeout(() => {
|
||||
build();
|
||||
}, 1);
|
||||
});
|
||||
|
||||
return { play, build };
|
||||
})();
|
||||
// @license-end
|
37
src/assets/_root/webtoys/bps/scripts/player.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0-1.0
|
||||
/****************************************************************************
|
||||
* BPS (Bill Paxton Soundboard)
|
||||
*
|
||||
* Copyright 2024 Eric Woodward
|
||||
* Source released under CC0 Public Domain License v1.0
|
||||
* https://www.itsericwoodward.com/licenses/cc0/
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
****************************************************************************/
|
||||
|
||||
// adapted from https://cheatcode.co/tutorials/how-to-build-a-soundboard-with-javascript
|
||||
|
||||
let sounds = [];
|
||||
|
||||
const injectPlayerIntoPage = (name, path) => {
|
||||
const player = document.createElement("audio");
|
||||
player.id = name;
|
||||
player.src = path;
|
||||
player.volume = 0.5;
|
||||
player.type = "audio/mpeg";
|
||||
document.body.appendChild(player);
|
||||
},
|
||||
load = (name, path) => {
|
||||
sounds = [...sounds, { name, path }];
|
||||
injectPlayerIntoPage(name, path);
|
||||
},
|
||||
play = (name) => {
|
||||
const player = document.getElementById(name);
|
||||
if (player) {
|
||||
player.pause();
|
||||
player.currentTime = 0;
|
||||
player.play();
|
||||
}
|
||||
};
|
||||
|
||||
export { load, play };
|
||||
// @license-end
|
1
src/assets/_root/webtoys/bps/scripts/rootdir.js
Normal file
@@ -0,0 +1 @@
|
||||
export default "/webtoys/bps";
|
BIN
src/assets/_root/webtoys/bps/sounds/aliens_17_days.wav
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/aliens_current_affairs.wav
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/aliens_express_elevator.wav
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/aliens_game_over.wav
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/true_lies_navel_lint.wav
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/true_lies_pathetic.mp3
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/weird_science_booze.mp3
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/weird_science_dead_meat.mp3
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/weird_science_he_pukes.mp3
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/weird_science_sandwich.mp3
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/weird_science_stewwed.mp3
Normal file
BIN
src/assets/_root/webtoys/bps/sounds/weird_science_turd_brain.mp3
Normal file
111
src/assets/_root/webtoys/bps/styles/styles.css
Normal file
@@ -0,0 +1,111 @@
|
||||
/* @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0-1.0 */
|
||||
/****************************************************************************
|
||||
* BPS (Bill Paxton Soundboard)
|
||||
*
|
||||
* Copyright 2024 Eric Woodward
|
||||
* Source released under CC0 Public Domain License v1.0
|
||||
* https://www.itsericwoodward.com/licenses/cc0/
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
****************************************************************************/
|
||||
|
||||
html {
|
||||
color: #DDE6ED;
|
||||
background-color: #161f2b;
|
||||
font-size: 20px;
|
||||
margin: .5rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #abc1d3;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #799cb9;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #444b55;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.center { text-align: center; }
|
||||
|
||||
.circleWrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
border: 1px dashed;
|
||||
border-radius: .5rem;
|
||||
display: inline-block;
|
||||
font-size: .75rem;
|
||||
margin: 1rem;
|
||||
max-width: 8rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.figureButton {
|
||||
cursor: pointer;
|
||||
margin: 0.5rem;
|
||||
max-width: 5.5rem;
|
||||
}
|
||||
|
||||
.figureButton:hover {
|
||||
filter: brightness(1.5);
|
||||
}
|
||||
|
||||
.figureButton figcaption {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.figureButton img { max-width: 100% }
|
||||
|
||||
/****************************************************************************
|
||||
* Media Queries
|
||||
****************************************************************************/
|
||||
|
||||
@media all and (min-width: 950px) {
|
||||
|
||||
/* circular layout derived from https://stackoverflow.com/a/12817454 */
|
||||
|
||||
.circleWrapper {
|
||||
--d: 6.5em; /* image size */
|
||||
--rel: .75; /* how much extra space we want between images, 1 = one image size */
|
||||
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan)); /* circle radius */
|
||||
--s: calc(2*var(--r) + var(--d)); /* container size */
|
||||
height: var(--s);
|
||||
margin: 1rem auto;
|
||||
position: relative;
|
||||
width: var(--s);
|
||||
}
|
||||
|
||||
.figureButton {
|
||||
--az: calc(var(--i)*1turn/var(--m));
|
||||
height: var(--d);
|
||||
left: 50%;
|
||||
margin: calc(-.5*var(--d));
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform:
|
||||
rotate(var(--az))
|
||||
translate(var(--r))
|
||||
rotate(calc(-1*var(--az)));
|
||||
width: var(--d);
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
font-size: 1rem;
|
||||
margin: 4rem 1rem 1rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* @license-end */
|