Midievol is a generative music application that consists of a frontend (this repository) and a backend (midievol-api-deno). It generates evolving musical ideas based on evolutionary principles. Starting from an initial melody, the system creates new variations using random mutations and selects the best ones using fitness functions—custom scoring mechanisms that evaluate melodies according to user-defined criteria.
The application continuously loops and plays melodies, updating them each iteration with newly generated variations to simulate a never-ending, evolving composition.
Each time a new loop begins, the frontend sends the current melody to the backend, which performs the following steps:
-
Scoring the Melody
The original melody is evaluated using a set of fitness functions. -
Creating Mutations
A number of "children" melodies are generated by mutating the original. Mutations can occur at both the note level (pitch, position, length, and volume—though volume is currently ignored by the frontend) and the structural level (e.g., duplicating or deleting sections). -
Evaluating Children
Each child melody is scored using the configured fitness functions. The results are combined using weighted averaging—functions with higher weights have more influence on the final score. -
Selecting the Best
The system selects the highest-scoring melody from the pool of children and the original.
This entire process represents one generation. Multiple generations can be run in sequence during a single backend call, allowing the melody to evolve further with each iteration. The final result is returned to the frontend for playback.
-
Run the Backend API
Make sure the midievol-api-deno is running onhttp://localhost:8000. -
Install and Start the Frontend
Ensure Node.js is installed (download here).# Run this once during setup or whenever new dependencies are added npm install # Start the application npm start
-
Open the Application
Navigate tohttp://localhost:1234in your browser.
On launch, you'll see the Main Page, which includes:
- Score Summary: Displays unnormalized scores from each active fitness function, along with a normalized overall score (between -1 and 1).
- Voice Range Controls: Two rotary knobs to control three voices—bass, mid, and melody:
Max Bass: Sets the upper limit for the bass voice and lower limit for mid.Min Melody: Sets the upper limit for mid and lower limit for melody.
- Visualization Visualization of the output sent to the MIDI output selected in the
Visualisation outputselect on the Details page
Accessed via the gear icon on the Main Page, the Details Page offers fine-grained controls.
- Configure output MIDI device.
- Set number of children per generation.
- Set number of generations per evolution cycle.
- Save/load settings and melodies.
- View melody stats: note count, length (in quarter notes), and score.
- Adjust BPM (tempo).
- Voice range controls (same as Main Page).
- Assign weights to fitness functions.
- Choose which voices each function evaluates (bass, mid, melody), represented by 3 checkboxes.
- Modify each function’s custom parameters.
- Visual graphs showing how each fitness function scored melodies across previous generations.
Use the home icon to return to the Main Page.
In the backend, fitness functions are called ScoringsFunction; in the frontend, they’re referred to as ModFunc. They have the following signature:
// Actual Note implementation has some methods not relevant for fitness functions
interface Note {
pitch: number;
length: number;
volume: number;
position: number;
}
export type ParamType = "note" | "float" | "int";
export interface Param {
name: string;
value: number;
range: [number, number]; // min and max setting for this param
type: ParamType; // Used for display in frontend
}
interface ScoringsFunctionArgs {
melody: Note[];
params: Param[];
voiceSplits: { min: number; max: number };
voices: [boolean, boolean, boolean];
}
export type score = number | null;
export type ScoringsFunction = (args: ScoringsFunctionArgs) => score;Each function receives:
- The melody to evaluate.
- Voice range splits and selected voices.
- An array of parameters (unique per function).
Functions return either a number (the score) or null if the melody is not scorable. null values are ignored during score aggregation.
The frontend only produces MIDI output. To hear the results, connect virtual or physical instruments:
- Set your MIDI output in the Details Page (
Outputselector). - The application sends MIDI on channels 1, 2, and 3, corresponding to the three voices.