Could you use a chess game as a music sequencer? I have in mind live generative pieces generated while you play, perhaps using a smart chess board as the interface. Some experiments at how this could sound…

We will use the Chess paclet to perform the implementation.

PacletInstall["Wolfram/Chess"]
Needs["Wolfram`Chess`"]

A quick overview of the Chess paclet

Random boards (i.e., after a certain number of randomly chosen moves) can be generated:

b = RandomChessboard[10]

You can also load board states from a FEN description

b = Chessboard["2kr1b1r/ppp1qppp/2n2n2/3p1b2/3P1N2/2P1B3/PP1NQPPP/2KR1B1R b - - 3 11"]

The resulting board has a character array:

b["CharacterArray"]

(*{ {"\[BlackRook]", "\[BlackKnight]", "\[BlackBishop]", " ", "\[BlackKing]", "\[BlackBishop]", "\[BlackKnight]", "\[BlackRook]"}, {"\[BlackPawn]", " ", " ", "\[BlackPawn]", "\[BlackPawn]", " ", "\[BlackPawn]", "\[BlackPawn]"}, {" ", "\[BlackPawn]", " ", " ", " ", " ", "\[WhiteKnight]", " "}, {"\[BlackQueen]", "\[WhiteKnight]", "\[BlackPawn]", " ", " ", "\[BlackPawn]", " ", " "}, {" ", " ", " ", " ", " ", " ", " ", " "}, {" ", " ", " ", " ", " ", " ", " ", " "}, {"\[WhitePawn]", "\[WhitePawn]", "\[WhitePawn]", "\[WhitePawn]", "\[WhitePawn]", "\[WhitePawn]", "\[WhitePawn]", "\[WhitePawn]"}, {"\[WhiteRook]", " ", "\[WhiteBishop]", "\[WhiteQueen]", "\[WhiteKing]", "\[WhiteBishop]", " ", "\[WhiteRook]"}}*)

Chessgame holds collections of board states plus metadata:

game = Import["ExampleData/sample.pgn", {"ChessGames", 1}]

You can access the board state from the list of FENs in the game:

Take[#, 5] &@game["FENs"]

(*{"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1", "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2", "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2", "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3"}*)

Sonification 1: Sequential tones for each board position

Idea: Assign each board position a MIDI tone (from 1-64), and then play a note of duration 0.1 if the location is occupied and no note (or rest) if the position is unoccupied. To my mind this sounds like a kind of demented Philip Glass:

sonify[board_Chessboard] := With[
    {activePositions = board["CharacterArray"] /. {" " -> 0, _String -> 1} // Flatten}, 
    Sound@MapThread[SoundNote, {Range[8*8] - 32, 0.1*activePositions}]] 
 
sonify[fen_String] := sonify@Chessboard@fen
sonify[game_ChessGame] := Sound[ sonify /@ game["FENs"]]
sonify@game
Export["sonification01.mp3", %]

sonification01.mp3

Sonification 2: Pauses for absent pieces

Idea: Keep the assignments, but open locations are rests. This creates a bit more variety in when notes sound, and thus a bit less monotonous. To my mind this sounds a bit more interesting, like a demented John Cage:

sonify[board_Chessboard] := With[
    {activePositions = board["CharacterArray"] /. {" " -> 0, _String -> 1} // Flatten}, 
    Sound@Map[SoundNote[#, 0.1] &]@ (activePositions*(Range[8*8] - 32) /. {0 -> None})] 
 
sonify@game
Export["sonification02.mp3", %]

sonification02.mp3

Sonification 3: Only play attacked pieces

Idea: Only play notes corresponding to attacked pieces. This will mean that we don[CloseCurlyQuote]t have any sounds until the middle of the keyboard as the game progresses. There is a bit more variety, but you get long stretches in the middle that are quite repetitive:

attacked[s_String] := With[
    {replaceAlpha = Thread[ Alphabet[][[1 ;; 8]] -> Range[0, 7]*8], 
     replaceInt = Thread[ (ToString /@ Range[8]) -> Range[8]]}, 
    -32 + Plus @@ Characters[s] /. replaceAlpha /. replaceInt] 
 
attacked[b_Chessboard] := attacked /@ b["AttackedSquares"]
sonify[board_Chessboard] := 
   Sound@Map[SoundNote[#, 0.1] &]@attacked[board] 
 
sonify@game
Export["sonification03.mp3", %]

sonification03.mp3

Sonification 4: Drones

Idea: OK, enough with the piano keyboard—let us instead think of this as an additive synthesis type problem. Each position is a sine oscillator. We start with chaos (many overlapping tones and evolve to simplicity). Each row spans an octave. Unfortunately, this sounds super harsh.

divisions[start_] := Most@Subdivide[start, 2*start, 8] 
 
sonify[board_Chessboard] := With[
    {activePositions = board["CharacterArray"] /. {" " -> 0, _String -> 1} // Flatten, 
     frequencies = divisions /@ (2^# &) /@ Range[6, 13] // Flatten }, 
    Play[
     Total@Thread[ activePositions*Sin[frequencies 2 Pi t]], 
     {t, 0, 1}, SampleDepth -> 16, SampleRate -> 22000]] 
 
sonify@b
Export["sonification04.mp3", %]

sonification04.mp3

Comment: This sounds really harsh, even for a single state. I wouldn’t want to listen to this.

Sonification 4b: Closer tones

Idea: A neat trick with sine oscillators is the beating and interference patterns they form. So instead let us use that as a basis. Suppose all 8 rows span 1 octave: each row is a note, but within the row we have a subdivision until the next note. This should lead us to have lots of beating early on and then start to pick out individual sounds. This sounds like a demented Edgard Vare’se to my ear…there are some interesting modulations, but it is still too sonically crowded. And perhaps 1 second is too long for each move to occupy sonically:

divisions[start_] := Subdivide[start, 2*start, 8]
sonify[board_Chessboard] := With[
    {activePositions = board["CharacterArray"] /. {" " -> 0, _String -> 1} // Flatten, 
     frequencies = (Most@Subdivide[First[#], Last[#], 8]) & /@ Partition[#, 2, 1] &@divisions[256] // Flatten }, 
    Play[
     Total@Thread[ activePositions*Sin[frequencies 2 Pi t]], 
     {t, 0, 1}, SampleDepth -> 16, SampleRate -> 22000]] 
 
sonify@game
Export["sonification04b.mp3", %]

sonification04b.mp3

Sonification 4c: Sine oscillators on attack

Idea: What if we only turn on the sine oscillator when a piece is attacked? That should lead us to slowly dial in complexity. It still is quite muddle in the middle, but has a kind of bad B-movie theremin vibe:

attacked[s_String] := With[
    {replaceAlpha = Thread[ Alphabet[][[1 ;; 8]] -> Range[0, 7]*8], 
     replaceInt = Thread[ (ToString /@ Range[8]) -> Range[8]]}, 
    Plus @@ Characters[s] /. replaceAlpha /. replaceInt] 
 
attacked[b_Chessboard] := 
   Total@Map[UnitVector[64, #] &]@Map[attacked]@b["AttackedSquares"] 
 
sonify[board_Chessboard] := With[
    {active = attacked[board], 
     frequencies = (Most@Subdivide[First[#], Last[#], 8]) & /@ Partition[#, 2, 1] &@divisions[256] // Flatten }, 
    Play[
     Total@Thread[ active*Sin[frequencies 2 Pi t]], 
     {t, 0, 0.5}, SampleDepth -> 16, SampleRate -> 22000]] 
 
sonify@game
Export["sonification04c.mp3", %]

sonification04c.mp3

Other ideas:

  • Do something smarter with rhythm

  • Differentiate the types of pieces (not just presence or absence at a location)

  • Better elements of time–for example, have a long decay envelope so that stationary pieces die down to zero volume, and the moves are emphasized

  • Consider subtractive approach: locations of pieces are band pass filters acting on a big noise input

Seen elsewhere

ToJekyll["Musical chess", "mathematica music chess"]