This commit is contained in:
2025-10-12 15:21:36 +02:00
parent a56b089bb2
commit 9c6997e6be
9 changed files with 155 additions and 45 deletions

5
.gitignore vendored
View File

@ -22,3 +22,8 @@ dist-ssr
*.njsproj
*.sln
*.sw?
# Claude artifacts
.claude/
.clinerules
CLAUDE.md

View File

@ -4,9 +4,9 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Audio sample generator" />
<meta name="description" content="Poof: a sample generator" />
<meta name="theme-color" content="#000000" />
<title>Vending Machine</title>
<title>Poof</title>
</head>
<body>
<div id="app"></div>

View File

@ -1,5 +1,5 @@
{
"name": "vendingmachine",
"name": "poof",
"private": true,
"version": "0.0.0",
"type": "module",

View File

@ -1,4 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" fill="#000"/>
<text x="16" y="21" font-family="monospace" font-size="18" fill="#fff" text-anchor="middle" font-weight="bold">VM</text>
<circle cx="16" cy="16" r="8" fill="none" stroke="#fff" stroke-width="1.5"/>
<circle cx="16" cy="16" r="4" fill="none" stroke="#fff" stroke-width="1"/>
<path d="M16 4 L16 8 M28 16 L24 16 M16 28 L16 24 M4 16 L8 16" stroke="#fff" stroke-width="1.5" stroke-linecap="square"/>
<path d="M23 9 L20.5 11.5 M23 23 L20.5 20.5 M9 23 L11.5 20.5 M9 9 L11.5 11.5" stroke="#fff" stroke-width="1" stroke-linecap="square"/>
</svg>

Before

Width:  |  Height:  |  Size: 236 B

After

Width:  |  Height:  |  Size: 529 B

View File

@ -908,7 +908,7 @@ export class Benjolin implements SynthEngine<BenjolinParams> {
}
// Correlate cross-mod amounts
const crossModDelta = (Math.random() - 0.5) * mutationAmount;
const crossModDelta = (Math.random() - 0.5) * mutAmount;
mutated.crossMod1to2 = Math.max(0, Math.min(1, mutated.crossMod1to2 + crossModDelta));
mutated.crossMod2to1 = Math.max(0, Math.min(1, mutated.crossMod2to1 + crossModDelta * 0.7));

View File

@ -1,4 +1,4 @@
import type { SynthEngine } from './SynthEngine';
import type { SynthEngine, PitchLock } from './SynthEngine';
interface InputParams {
recorded: boolean;
@ -110,11 +110,11 @@ export class Input implements SynthEngine<InputParams> {
return [leftResampled, rightResampled];
}
randomParams(): InputParams {
randomParams(_pitchLock?: PitchLock): InputParams {
return { recorded: this.leftChannel !== null && this.rightChannel !== null };
}
mutateParams(params: InputParams): InputParams {
mutateParams(params: InputParams, _mutationAmount?: number, _pitchLock?: PitchLock): InputParams {
return params;
}
}

View File

@ -1,4 +1,4 @@
import type { SynthEngine } from './SynthEngine';
import type { SynthEngine, PitchLock } from './SynthEngine';
interface SampleParams {
loaded: boolean;
@ -66,11 +66,11 @@ export class Sample implements SynthEngine<SampleParams> {
return [leftResampled, rightResampled];
}
randomParams(): SampleParams {
randomParams(_pitchLock?: PitchLock): SampleParams {
return { loaded: this.leftChannel !== null && this.rightChannel !== null };
}
mutateParams(params: SampleParams): SampleParams {
mutateParams(params: SampleParams, _mutationAmount?: number, _pitchLock?: PitchLock): SampleParams {
return params;
}
}

View File

@ -102,7 +102,7 @@ export class WavetableEngine implements SynthEngine<WavetableParams> {
});
const results = await Promise.all(loadPromises);
const loaded = results.filter((wt): wt is Wavetable => wt !== null);
const loaded = results.filter((wt) => wt !== null) as Wavetable[];
if (loaded.length > 0) {
this.wavetables = loaded;

View File

@ -21,11 +21,23 @@
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
>
<h1 id="modal-title">Vending Machine</h1>
<h1 id="modal-title">Poof: a sample generator</h1>
<p class="description">
Oh, looks like you found a sound vending machine. This one seems slightly
broken and it seems that you can get sounds for free... Have fun!
Do you need to generate audio samples for your projects? Poof, it's
already done! These are not the best samples you'll ever hear, but they
have the right to exist, nonetheless, in the realm of all the random and
haphazardly generated digital sounds. Have fun, give computers some love!
</p>
<ul>
<li class="description">
Generate audio samples using various audio synthesis generators. Random
parameters.
</li>
<li class="description">
Process each sound with with a growing collection of random effects.
</li>
<li class="description">Export your samples as WAV files.</li>
</ul>
<div class="modal-links">
<p>
Created by <a
@ -53,107 +65,197 @@
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
background-color: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(4px);
display: flex;
justify-content: center;
align-items: center;
z-index: 2000;
animation: fadeIn 0.2s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.modal-content {
background-color: #000;
border: 2px solid #fff;
padding: 1.5rem;
padding: 1.25rem;
max-width: 500px;
width: 90%;
width: calc(100% - 2rem);
color: #fff;
max-height: 90vh;
overflow-y: auto;
animation: slideIn 0.3s ease-out;
box-shadow: 0 8px 32px rgba(255, 255, 255, 0.1);
}
.modal-content h1 {
margin: 0 0 0.75rem 0;
font-size: 1.5rem;
margin: 0 0 0.5rem 0;
font-size: 1.75rem;
font-weight: bold;
letter-spacing: 0.02em;
}
.modal-content .description {
margin: 0 0 1rem 0;
line-height: 1.5;
color: #ccc;
font-size: 0.9rem;
line-height: 1.6;
color: #e0e0e0;
font-size: 0.875rem;
}
.modal-content ul {
margin: 0 0 1rem 0;
padding-left: 1.25rem;
}
.modal-content ul li {
margin-bottom: 0.5rem;
}
.modal-content ul li:last-child {
margin-bottom: 0;
}
.modal-links {
margin: 1rem 0;
padding: 0.75rem 0;
border-top: 1px solid #333;
border-bottom: 1px solid #333;
margin: 1.25rem 0;
padding: 0.875rem 0;
border-top: 1px solid #444;
border-bottom: 1px solid #444;
}
.modal-links p {
margin: 0.4rem 0;
font-size: 0.85rem;
color: #ccc;
line-height: 1.4;
margin: 0.375rem 0;
font-size: 0.8125rem;
color: #bbb;
line-height: 1.5;
}
.modal-links a {
color: #646cff;
text-decoration: none;
word-break: break-word;
transition: color 0.2s ease;
}
.modal-links a:hover {
color: #8891ff;
text-decoration: underline;
}
.modal-close {
margin-top: 0.75rem;
margin-top: 1rem;
width: 100%;
padding: 0.65rem;
padding: 0.75rem;
font-size: 1rem;
background-color: #fff;
color: #000;
border: none;
border: 2px solid #fff;
cursor: pointer;
font-weight: bold;
transition: all 0.2s ease;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.modal-close:hover {
background-color: #ddd;
background-color: #000;
color: #fff;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(255, 255, 255, 0.2);
}
@media (min-width: 768px) {
.modal-close:active {
transform: translateY(0);
box-shadow: 0 2px 6px rgba(255, 255, 255, 0.1);
}
@media (min-width: 480px) {
.modal-content {
padding: 2rem;
padding: 1.75rem;
width: calc(100% - 3rem);
}
.modal-content h1 {
font-size: 2rem;
margin: 0 0 0.75rem 0;
}
.modal-content .description {
font-size: 0.9375rem;
}
.modal-links p {
font-size: 0.875rem;
}
}
@media (min-width: 768px) {
.modal-content {
padding: 2.5rem;
}
.modal-content h1 {
font-size: 2.5rem;
margin: 0 0 1rem 0;
}
.modal-content .description {
margin: 0 0 1.5rem 0;
margin: 0 0 1.25rem 0;
font-size: 1rem;
line-height: 1.6;
line-height: 1.7;
}
.modal-content ul {
margin: 0 0 1.25rem 0;
}
.modal-links {
margin: 1.5rem 0;
padding: 1rem 0;
margin: 1.75rem 0;
padding: 1.125rem 0;
}
.modal-links p {
font-size: 0.9rem;
font-size: 0.9375rem;
margin: 0.5rem 0;
}
.modal-close {
margin-top: 1rem;
padding: 0.75rem;
font-size: 1.1rem;
margin-top: 1.25rem;
padding: 0.875rem;
font-size: 1.125rem;
}
}
@media (prefers-reduced-motion: reduce) {
.modal-overlay,
.modal-content {
animation: none;
}
.modal-close {
transition: none;
}
.modal-links a {
transition: none;
}
}
</style>