Raphaël Maurice

Forment Eclla

Jack Armitage

Sardine: a Modular Python Live Coding Environment

Sardine is a live coding environment and library for Python 3.10+ focusing on the modularity and extensibility of several base components (clocks, parsers, handlers). Sardine has been designed to be easily integrated with existing live coding environments as both a tool for experimentation, and a demonstration of various live coding techniques: temporal recursion, patterning, integration in various hardware and software setups. Although the tool is still in active early development, it has already been used in multiple public performances and algoraves, partly enabled by its support for MIDI IN/Out, OSC IN/Out and SuperCollider/SuperDirt one-way communication through OSC. This paper is dedicated to the introduction of the Sardine system, and the explanation of the main guidelines currently followed by contributors to the project. It will also present the preliminary results of our work through practical realisations that served as experimental validation during the early stages of development.

Introduction

Sardine is a musical live coding library based on Python 3.10+ focusing on modularity and extensibility of several base components. Despite the fact that the project is not yet one year old, Sardine is already extensively documented on a dedicated website providing installation guides, documentation and tutorials. Sardine provides three main features built over the basic Python interpreter and linked together by the FishBowl -an environment handling synchronisation and communication between them:

• A scheduling system based on asynchronous and recursive function calls inspired by the concept of temporal recursion [START_REF] Sorensen | The Many Faces of a Temporal Recursion[END_REF]. Calls can be scheduled in musical time either on an InternalClock or a LinkClock based on the Link Protocol [START_REF] Goltz | Ableton Link-a Technology to Synchronize Music Software[END_REF]).

• A modular handler system allowing the addition and/or removal of various inputs/outputs (e.g. OSC, MIDI) or base components through a central dispatch environment named the FishBowl. This allows the customisation of IO logic, without the need to rewrite or refactor low-level system behaviour.

• A general purpose number-based algorithmic pattern language with support for generative constructs. This language allows the playful manipulation of melodies, audio samples, synthesizers, synthesis parameters and data used by the various components to create algorithmic music.

Sardine, by design, is in the direct lineage of previously released Python based libraries such as FoxDot [START_REF] Kirkbride | Foxdot: Live Coding with Python and Supercollider[END_REF], Isobar (Jones, n.d.) or the very recent port of TidalCycles in Python, TidalVortex [START_REF] Mclean | TidalVortex Zero[END_REF]. At its inception, the Sardine project was thought as an attempt to provide a functional but barebones live coding library to be used for demonstration purposes in a dissertation manuscript. Sardine partially emulates some selected features from the previously mentioned libraries or from the dominant live-coding dialects such as the Tidal pattern mini-notation [START_REF] Mclean | Making Programming Languages to Dance to: Live Coding with Tidal[END_REF] or the Sonic Pi imperative scheduling syntax [START_REF] Aaron | Sonic Pi, Performance in Education, Technology and Art[END_REF]. To serve its initial purpose, the library had to be capable enough for showing the impact of design and implementation choices on the possibilities of musical expression and on the expressiveness offered by a live coding environment. In consequence, Sardine has been designed as an agnostic framework for approaching live coding using Python. The library aims to support different writing paradigms and different approaches to live performance based on the manipulation of source code. The reliance on regular Python asynchronous functions for scheduling and music writing means that Sardine is particularly suited to let each developer-musician build their own personal coding style, providing a blank slate for experimental interface building. Furthermore, Sardine's design has been strongly influenced by McPherson and Tahiroğlu's concerns about the idiomatic patterns (McPherson and Tahıroğlu 2020) of usage enforced by computer music software, pushing users to repeat and strictly follow prefered patterns of usage. Sardine focuses on laying out the base infrastructure needed to support live coding in Python and wishes to encourage users to imagine diverse patterning idioms and live coding targets, mini-notations or user-facing scheduling mechanisms and syntax. The system's modular architecture is a first step towards the inclusion of more targets and custom input and output handling. Initial work for the 0.1.0 has been based upon an older personal attempt at writing a live coding library, then named ComputerTalk¹. ComputerTalk's base design was not suitable with Sardine's goal, and so the base has quickly evolved after the first initial public tests, aiming for increased modularity of the system in order to maximise IO options.

The version hereby presented -v0.3.0 at the time of correction -offers a first look into the complete intended design for the library, and is a near complete rewrite over the 0.1.0 version previously used by members of the French live coding scene and by the first global Sardine users. It features two different clock implementations, multiple handlers for IO (MIDI, OSC, SuperDirt), a robust asynchronous temporal recursive scheduling system, and a reimagining of the Player system previously introduced by FoxDot (Kirkbride 2016). Since february 2023, Sardine also includes a prototype parser for the Ziffers language for musical pattern notation developed by Miika Alonen. Sardine's originality lies in the implementation of its temporal model, strongly anchored in Python's default mechanisms for asynchronous programming. It has been developed collectively with the help of John Phan, based on user requests and feedback gathered during a first period of experimentation that saw Sardine being used or integrated by musicians for several algoraves, network-based jams and musical performances. Sardine has been developed exclusively using the Python programming language.

Some libraries, providing critical functionality such as MIDI I/O or network synchronisation are obtained through Python bindings to C++ external libraries. Despite the known shortcomings of Python for interpreted real-time programming (incomplete support of dynamic programming, slowness relative to other interpreted languages), we believe that this language is suitable for the implementation of a live coding library. The large collection of available libraries and modules and the popularity of the language ensures the affordance of good tooling and rich customization and integration options for different text editors, running environments, and more. Sardine already takes advantage of a thorough ecosystem of libraries focused on data input/output, network communication and text manipulation. Moreover, thanks to its lightweight and clear syntax, Python can be read by programmers coming from various backgrounds, making it a convenient platform for collaboration and experimentation with bespoke features needed by performers.

In the present article, we will introduce Sardine by detailing its goals (1) and base implementation centered on the scheduling mechanism (2), the environment/handler system (3) and the mininotation support (4). By doing so, we hope to highlight the basic principles of its inner workings, while providing some context on the current direction taken by the project and by its users.

Methodology and Abjectives: a Framework for Exploring Live-Coding in Python

The development of Sardine began initially in a period of active collaboration and joint performances with the Parisian Cookie Collective ("Cookie Collective" 2016) and the Digital Audio Community from Lyon (France). Stemming from the demo and shader-coding scenes, Cookie Collective's members are known for their complex multimedia performances. Each member typically relies on bespoke hybrid audio-visual setups ranging from low end computing devices to complex synthesizers and circuit-bent video mixers. The Cookies are also known for working in an improvised manner, customising setups for each venue depending on audience needs and expectations. This need for customisation gave rise to the idea of a modular interface that could be used and mastered by every member of the collective, while allowing for jam-ready synchronisation with other musicians and live-coders. The recent splitting of FoxDot's development into several scattered branches reinforced the need for a central, customisable and easily editable Python interface, the library being used particularly in the French live coding community.

Due to an open-ended development process, Sardine has been gradually shifting towards its current modular architecture, allowing each performer to refine the system, from simple MIDI note output to more convoluted SysEx and OSC handling. The invaluable help and expertise from John Phan has allowed for a rewrite of every base mechanism. The finalisation of Sardine's new framework allows focus to turn towards introducing new features and improving existing ones, with users being encouraged users to propose ideas and contribute code and documentation.

Having completed most of its initial objectives as a demonstration tool -the imitation and reproduction of known mechanisms -Sardine is now gradually shifting towards a more creative open-ended development process. This process is twofold: to facilitate the installation, learning and use of the software on the one hand, and to extend its expressive capabilities on the other. Although these two tasks can be seen as related, the former has more to do with the inclusion of new users, while the latter has to do with creative exploration for the more experienced users. In order to strengthen the accurateness of our aim, we have chosen to present Sardine to various audiences (at PyCon France, in hackerspace workshops or informal musician meetings) in order to study user's usage patterns and expectations when confronted to the software. This has led to many multi-platform support fixes and improvements. This has also led to improve the usability of Sardine without a dedicated text editor or to improve the quality and quantity of documentation provided with the software.

Sardine Implementation and Installation

Figure 1: Software layers of the Sardine system stack.

Sardine is implemented and distributed as two complementary Python modules: sardine and fishery. The fishery module is a modified version of the base Python asynchronous REPL². Starting fishery automatically imports Sardine and starts a new live session. To facilitate configuration and orchestration of the various components of the system, a terminal based configuration client, sardine-config, is also provided. This tool is designed as a user-friendly interface for the editing of configuration files. Configuration files are stored in a single folder inside a OS-specific default standard location (e.g. .local/share on UNIX systems), and include a main JSON file, a blank .py file used for loading custom Python code among other files needed to properly configure a SuperCollider or SuperDirt session. Despite its initial complexity, this approach makes Sardine more accessible to novice users who may not be familiar with the command line, with Python development tools or with plain-text editing of configuration files. In order to encourage modularity in user's configuration, many of the input and output components are disabled by default. This makes the installation of other programs like SuperCollider entirely optional while also encouraging users to consider their needs for a successful interaction with their own instruments and softwares.

Sardine does not require usage of a specific editor or text-editor plugin. Any IDE³ or coding environment capable of spawning a command line interface and sending code to it is sufficient. Fortunately, these features are usually included by default in most popular code editors or Python-specific plugins⁴. With this approach, Sardine has been successfully tested with Atom, VSCode, Emacs, Vim/Neovim and Jupyter Notebooks. Sardine uses the Python interpreter mainly as a code receiver and a monitoring tool for displaying useful information to the user, such as the state of a SuperCollider sub-process, or of an event loop. Since january 2023, some effort has been put into the development of an internal text editor named fishery web for its reliance on web technologies. This editor uses the Flask Python web application framework to serve a text editor web application composed using the Svelte frontend library and WebSockets [START_REF] Fette | The Websocket Protocol[END_REF] for communicating between the web browser and the local fishery instance. This text editor, fully dedicated to newcomers, already includes automatic handling of text files and quick access to documentation.

Reliance on an audio backend requires booting another application. So far, SuperCollider and SuperDirt are natively supported to do this by their own Sardine components. Even though the installation of these backends is still necessary for users willing to use them, integration is done in such a way that there is no need -later on -to actively take care and monitor any of these dependencies. A basic interface to SuperCollider is offered through the SC() function, allowing to run arbitrary sclang code in the subprocess session. A more thorough integration with SuperCollider is planned for inclusion in future versions of the software. Clever usage of the provided tools for MIDI and OSC communication already allows users to generate custom mappings to other custom audio custom backends (Max/MSP patches, DAW, etc).

Sardine is packaged as a regular Python module, making use of the pyproject.toml module configuration and packaging format defined by PEP 660, meaning only a base Python 3.10+ runtime is required for installation. Sardine has recently been packaged and released on PyPi, allowing installation using a simple command: python -m pip install --find-links https://thegamecracks.github.io/python-rtmidi-wheels/ sardine-system. All C++ dependencies and wheels -binary distribution of compiled packages -are available for every major platform. This also means Sardine is compatible with the recent release of Python 3.11. Sardine makes use of Python's asynchronous programming features, specifically the asyncio REPL prototype introduced by Python 3.8 (Selivanov 2019). The UVLoop [START_REF] Selivanov | UVLoop[END_REF] drop-in replacement event loop is also used in order to speed up asynchronous call scheduling (except on Windows). Several hot-patches to the asynchronous loop have been introduced by John Phan (thegamecracks) to make its behaviour consistent on every major OS platform. Sardine is laid out as a series of abstractions built on the base loop, making it aware of tempo and timing. Sardine's clock (either internal or link) automatically starts whenever the system is imported, but pure asynchronous calls can still be handled even if the clock is stopped. The LinkClock allows Sardine users to connect their session to a global tempo on a local network, enabling networked synchronisation of several Sardine instances and/or other Link-enabled devices⁵. All Sardine clocks provides the same interface, allowing the system to retrieve the current bar, beat and position in musical time. Time drift compensation and time shifting needed by some features is handled by the low-level event loop system. Asynchronous clock consistency is covered by tests (in the tests/ folder) and favourably compares with alternatives offered by similar, more widely used threaded clocks. Development of such a feature has proven to be a difficult technical challenge due to the specificity of the task, and of the relatively obscure and scarcely documented inner workings of each OS's internal schedulers. Threaded components are still used for various IO operations to lighten the load of the event loop and to alleviate the temporal cost of message processing. Many Sardine components are optional and can be activated on demand by the user, apart from the clock, AsyncRunners and SleepHandler core abstractions needed for asyncio loops. Basing Sardine's custom event loop on Python's asynchronous interpreter allows for the evaluation of any top-level asynchronous await instructions that would be forbidden by the main interpreter. However, Python asyncio ultimately behaves differently on every major OS due to its binding with system-level mechanisms, and more empirical testing is needed to document OS-specific limitations of this approach. Python does not feature native tail-call recursion support (Rossum 2009a(Rossum , 2009b)), making the infinite recursion of a function, as Sardine requires, a delicate task. Support for this central feature is based on John Phan's AsyncRunners, which is the basis for every repetitive operation -such as a repeating pattern -scheduled within Sardine. In the spirit of the nautical theme of Sardine, a temporal recursive function is called a swimming function and is labelled in code as an AsyncRunner. Swimming functions can be started using the @swim decora-tor⁶, stopped using the @die decorator, and can receive updates throughout its lifetime on the scheduler (see Figure 3).

Event Loop and Scheduling System

Scheduling

Decorated asynchronous Python functions are passed to the scheduler, making them repeat every p (for period), a time measured in beats relative to the clock currently in use. The content of a given function will be re-evaluated for every recursion cycle and state can be preserved either by passing arguments to a subsequent call or by relying on global state. Swimming functions are a powerful construct for building abstractions dealing with time, code re-evaluation and dynamic lifetime management of code components. Iterators, for example, can be built by incrementing a variable passed as an argument. Random generators can be built by calling a simple native random function whose result will be dynamically updated for each recursion.

Swimming functions will automatically start on-the-beat. The start of a function can target a specific point in musical time by specifying a special snap argument that is intepreted as an offset in beats from the beginning of the next bar. The until keyword can also be used to give to each swimming function an explicit end after n iterations. The ⁵Sardine has also been integrated with Flok (https://github.com/munshkr/flok) made by Damián Silvani. This tool for remote live-coding allows any number of users to play by sharing the same code and code evaluation events remotely through the network without sharing the same clock or local audio backend.

⁶Decorators in Python are used to add a behaviour to an object such as a function without modifying the base object itself.

period argument of a given function is the only required argument for a function to be considered as a valid swimming function. All Sardine components are based on the assumption that their evaluation context will be as a swimming function. They can receive any arbitrary Python code and/or call the various players defined by the Sardine system to handle IO operations. Thus, the prototype of a basic musical function using the previously defined model looks like the following:

@swim # swimming decorator (swim or die) def swimming_function(p=0.5, i=0):

# p: (period), i (custom iterator) print('I am swimming in time. Multiple abstractions can be built on top of the basic swimming function mechanism, allowing for a terse user-facing syntax. Building abstractions on top of @swim is helpful to allow newcomers to grasp the system's temporal model. The FoxDot-inspired surfboard mechanism is the first available abstraction demonstrating this principle. It automatically handles its own scheduling logic and provides its own iterators to the default parser. As demonstrated by the following example, it also provides customised musical logic, thus adding a completely new flavour of patterning and scheduling over the basic swimming functions. Following this model, Sardine future versions are likely to include user-based playing modes built upon the basic abstractions provided by the library. The experimental Ziffers notation integration to Sardine relies on a similar design pattern, the customization of swimming functions. Worthy of note is Sardine's sleep() method, which has been overridden from the default Python time.sleep() function that would, if used, block the event loop. This function defers the execution of any statement or expression defined thereafter to x beats in the future, even if these events take place after the next recursive call, a phenomenon known as oversleeping. Unlike time.sleep(), Sardine's sleep() does not block the function from running to its end, instead it temporarily affects the value of clock.time and extends the perceived time of methods using that property. This mechanism mimics the sleep() statement found in other live coding tools such as Sonic Pi [START_REF] Aaron | Temporal Semantics for a Live Coding Language[END_REF]. This mechanism can be used explicitely by the user or implicitely by functions implementing algorithmic transformations of a simpler event.

Environment, Dispatch and Handlers

The FishBowl

While scheduling takes an important role in the overall modular design of the Sardine library, its logic wouldn't function without a central piece of the system called the FishBowl. The FishBowl is an environment for software components, and handles synchronisation and coordination between all the different pieces composing a Sardine system. It is designed so that every component of the system can talk and instantly access data held by any other component. The FishBowl allows components to subscribe to it through hooks, and to react to messages through a dispatch system. The bowl.dispatch('stop') message is an example of such an event which stops the clock and requests for the collaboration and immediate response of multiple components. Naturally, some components are more important than others and can thus be considered as hard dependencies. Other soft dependencies, mainly the various IO handlers available, can be added and removed from the environment or session at any point in time. The clock and the parser are two hard dependencies that cannot be completely removed, but can be swapped. They provide the basic mechanisms needed by other modular components to properly function. The fluidity of the FishBowl mechanism allows for the addition and removal of modular logic to any Sardine system capable of answering to any message currently being dispatched to other components. One can switch from the internal to the link clock on-the-fly if the need arises to synchronise with other players, or add a new OSC receiver.

Case-study of a component: the MIDI sender

In the previous code example, a MIDI handler was added to the FishBowl, giving access to a new MIDI output. Senders are one type of Sardine modular components which requires the collaboration of multiple parts of the system to function properly. The N (midi.note_send) is one of the possible outputs and user interface offered by this component. To operate efficiently, it requires an access to the parser for patterning and composing a valid message, to the clock for sending its message in musical time, and to the SleepHandler to precisely time calls between a Note On and Note Off message. By declaring itself to the environment, it gains access to these required features, that will be accessed transparently without having to deal with lower-level logic. Consequently, user interaction can be implemented through one minimal function only, letting the system handle the hard and slightly convoluted asynchronous scheduling calls taking place in the background.

# basic MIDI note scheduling (duration handled by bowl.SleepHandler) N(note=60, velocity=100, channel=0, dur=0.25) # patterning a similar call with added component-specific logic (strings parsed by bowl.parser) N(note='C@penta C.. G3', velocity='80~100', channel='[0:10]', i=i, r=2) Similar senders or handlers can be implemented for various operations requiring collaboration between multiple parts of the system. Given that each one of these adhere to the BaseHandler abstract base class, adding a component to Sardine does not require refactoring of the base system. Most of the internal critical components of Sardine work by taking advantage of interconnection of every component, allowing one component to affect the behaviour of the whole environment, if needed. Given that handlers generally implement one specific input or output protocol, one handler is generally responsible for providing many different senders (e.g MIDI note, MIDI control change, MIDI program change, etc) as well as custom variants (Ziffers SuperDirt interface, etc).

Sardine Pattern Language

Sardine Sefault Pattern Language

Initially for demonstration and usability purposes, a small domain-specific language (DSL) for musical patterns has been developed for Sardine using Lark (Shinan n.d.) for LALR parsing. The DSL's source code is directly included in the sardine module, in the sequences/ directory. A DSL was needed to deal with the limited support provided by Python for syntactic macros (à la Lisp) and operator overloading. The use of the hard parser dependency is limited to parsing string arguments provided to any handler's send methods. These send functions, common to any sender, act as its principal interface for patterning and output alike, enabling the creation of complex data and music patterns, evolving over time in the context of a swimming function. Patterns play an important role in the workflow of audio/visual live coders, allowing them to define rich evolving structures spanning over time [START_REF] Magnusson | Performing with Patterns of Time[END_REF]. A generic interface P() is also available to increase the patternability of Python code or function calls done in the context of recursive swimming calls. This basic pattern language is best defined as a rich and terse interface dealing with lists of arbitrarily typed elements ranging from numbers to MIDI notes, samples or synthesizer names, or even OSC addresses. Pattern strings given for each keyword argument provided to a send method are resolved by the parser as arbitrarilynested lists, which are in turn used for composing an output message. Quite possibly, multiple connections to different parsers will be supported in future versions as a way to vary the idioms available for composing patterns. For now one parser is supported in each instance of the FishBowl, and its use is optional.

# standalone call to the parser, yielding one value per iteration (i acting as the index) P('1 2 3 2~40 5!4 C@fifths', i=i)

# several calls nested inside a more densely composed call to SuperDirt. D('amencutup:[1:10]', legato='0.1~0.8', room='0.5', dry='[0.1,0.5:0.05]', i=i) Extensive support has been dedicated to list-based operations for the composition of sequences. Binary arithmetic operators such as +, -, * and % can work either on single tokens or on lists (on both sides). Lists can be arbitrarily nested. List slicing and value extraction has been re-implemented in a fashion similar to that of Python. Unary operators such as (abs x), (sin x) or similar scientific calculation functions work in a similar way, with the function being mapped to each element of the list if needed. Basic function calls are available, opening the pattern language to more complex operations. Functions can support arbitrary number of arguments and keyword arguments (e.g. (disco F A C E ::depth 2 ::cond (beat 1))). Custom operators have also been defined such as x~y (choosing a number in range), x|y|z (choosing between x elements). Other operators have been borrowed to similar pattern languages such as Ziffers (Alonen n.d.) and TidalCycles: ' for octave up, . for octave down, : for sample choice, ! for repetition, among many others. Basic music notation is handled through the conversion of specific tokens to single MIDI notes (C#4 or D#4 parsed as 61), silences (a full stop . or variable size ellipsis ...), list objects (C@penta parsed as [60,62,64,67,69]) with support for transposition, chord and structure inversion. A complete list of all supported operations is provided to the user through Sardine's documentation. Support for random and generative structures, albeit basic, has been implemented. Again, the implementation of this feature has been facilitated by the definition of the parser as a component of the FishBowl. This allows the parser to query the environment and bowl.clock in search of semi-random number generators, such as the measure number ($.m), phase ($.p) or current beat since the origin of time ($). Similar functions, such as (phase 0.25 0.5) are implementing conditional operations based on the available timing information.

# Middle-C MIDI Note with default velocity and channel (N, alias for midi.send_note) N(60) # C major natural seventh chord with velocity in between 80 and 120, channel either 0, 1 or 2 N('{C@maj7}', vel='80~120', chan='0|1|2', i=i) # SuperDirt call, picking samples '0' to '20' in order in the 'drum' folder. Speed parameter # ramping from 1 to 10 in increments of 2, shape is the sin function of current time divided by 2. D("drum:[0:20]", speed='[1:10,2]', shape='(sin $)/2', i=i)

Iteration and pattern querying

Querying values from the possible multiple patterns per swimming function is done by providing a single pattern-wide iterator (labelled i) as an argument to each sender. Indexing errors are taken care of by making this index cyclical over the length of each pattern. The design of the iterator is a key creative choice for the user. Hence, the preferred method for browsing through the reduced list patterns can be chosen depending on context: sequentially, in reverse, or using a random number generator. Additional arguments, namely rate and div, can help in specifying how the iterator will be applied to the gathered patterns, adding another quickly accessible layer of patterning. The rate argument (aliased to r) is used to compress or extend the number of iterations needed to move from a list index to the preceding/next. It is best understood as a way to slow down or speed up the iteration over patterns. The divisor argument (aliased to div or d) is a modulo operation between the iteration count and div that will determine if the event is to be played. Other sender-level functions, similar in functionality to high-level functions applied to musical patterns from live coding languages such as TidalCycles, have also been experimented with in the most recent versions of Sardine. While the querying of pattern information is done by usage of iterators, this new set of function will use other available temporal information -such as current bar or phase -to apply transformations to patterns based on a temporal condition.

@swim def querying_flavors(p=1/2, i=0): D('bd cp sn hh', i=i, r=0.5, d=2) # simple iterator: 0, 1, 2. # Custom rate and div D('linnhats!4 jvbass', i=-i) # reversed iterator: 0, -1, -2 D('bongo bongo:2', i='rand*50') # random iterator: 0, 24, 5 again(querying_flavors, p=1/2, i=i+1)

Figure 10: Querying pattern values using iterators in swimming functions

The iterator-based pattern system appears to be well suited to a system based on temporal recursion, partly as recursive operations are often used as iteration tools in functional approaches to programming. Multiple iterators can be used in the same pattern by playing around with the P() mechanism previously described. This allows for the creation of arbitrarily complex patterns composed of multiple values assigned to any parameter accessible through a given sender. Even though the list of features provided by the pattern language is dense, its architecture is not complex and allows for quick customisation and feature addition. As a downside, the Sardine Pattern Language syntax and mode of operation can prove particularly difficult to learn for new users as it requires a good understanding of both recursion and iteration, the two concepts being used extensively in the overall design of the basic components. Being modal and modular in nature, understanding how to apply patterns to both swimming functions and Players (shorthand notation) can also prove difficult for new users.

Planned extensions of the parser mechanism

The basic Sardine parser, in spite of its experimental status, is already useful for increasing the general playability of the system and for generating complex musical algorithmic patterns. Multiple extensions and refinements will come in future versions, specifically aimed at making the pattern language more easy to write and less error prone for the end-user. New additions will come in two categories, the first focusing on additions and improvements to this pattern language, and the second on adding new or different pattern languages. Support for the Ziffers numerical notation created by Miika Alonen, re-implemented in Python, is in progress and can already be used thanks to the addition of an additional package, ziffers-python. To be supported, Ziffers requires new types of Players, relying on Sardine's lowlevel scheduling mechanisms. Supporting Ziffers is helpful to test the ease of integrating new writing and patterning paradigms into Sardine, while -in the process -gaining access to new ways to write melodic and harmonic patterns. The addition of the CLCE (Common Lisp Compositional Environment) Time Generators, a non documented library for algorithmic composition written by Stéphane Letz and Yann Orlarey (GRAME CNCM⁷, France) is being considered as a way to preserve the legacy and compositional ideas explored by this project. This would make use of Sardine as a tool for the study of algorithmic composition while allowing the exploration of more functional approaches to pattern generation. Over its relatively short timespan, from September 2022 to March 2023, Sardine has already been used multiple times for public music performances. The thrill and danger arising from using an unstable and unpolished software has constituted an obstacle as well as an appealing perspective for most users [START_REF] Roberts | Tensions and Techniques in Live Coding Performance[END_REF]. Jams, informal meetings and dialogue with the wider live coding community were also of the utmost importance to study the integration of Sardine in various musical and technical environments. Here we mention a few specific examples of Sardine usage, and show how being able to live code in Python provides unique advantages (and also potential disadvantages). Numerous specialised devices or handlers have been created to facilitate Sardine interacting with other systems. Hybridations between multiple live coding environments, such as Sardine/TidalCycles and Sardine/Sonic Pi have been tried successfully. Initial failures mostly arised from improper or careless implementations of the synchronisation mechanism. Documentation pertaining to the implementation of live-coding oriented musical clocks and synchronisation mechanism is scarce and most of it had to be inferred from the inner workings of similar open-source libraries. In order to help with the collective effort of documenting live coding practices, we hope to upload a very thorough explanation of the system on the Sardine website to help future developers. A proto-sender specialised in SysEx communication with a Yamaha TX7 synthesizer unit has been designed by Raphaël in order to enhance melodic and timbral capabilities of Ralt144MI (Rémi Georges) current live coding setup, previously mostly based on TidalCycles, MIDI controllers and audio-video hardware. Using a simple dictionary of lambda functions, this mechanism uses the previously described P() function to provide a general patterning interface for each individual parameter defined in the MIDI specification of the unit. Subsequent performances led to the inclusion of more bespoke mechanisms that ultimately made their way into the main codebase, having been proved useful in the context of live performances (span, snap, support for polyphony, etc…). This experience also proved the usefulness of adding better support for the definition of custom-fit senders and output/patterning interfaces.

At the Intelligent Instruments Lab in Reykjavík, Jack Armitage has been using Sardine to live code the Magnetic Resonator Piano (MRP) [START_REF] Mcpherson | Augmenting the Acoustic Piano with Electromagnetic String Actuation and Continuous Key Position Sensing[END_REF]. In this case, Sardine enabled developing a Python API client for the MRP much more interactively, due to being able to redefine @swim functions on the fly. Another attractive feature of Sardine being written in Python in this case, is that the entire Python ecosystem can be explored for machine learning based approaches to instrumental control and interaction⁸. This also positions Sardine as a bridging platform for researchers and practitioners interested in hybrid live coding interfaces, as in this case where live coding and instrumental practices are blurred. Jack Armitage has also been live coding artificial life simulations using Sardine, in the Tölvera library as part of the Agential Scores project [START_REF] Armitage | Agential Scores[END_REF]. This is one of the first examples of Sardine being used for visual output. In this case, the Taichi numerical simulation library was able to run its animation frame loop as a @swim function, with Sardine's recursion occurring fast enough to give 60fps graphics⁹ (see Figure 12). These early examples showed the diversity of approaches that are possible in the Python ecosystem, and they also usefully highlighted usability pain points and performance bottlenecks with Sardine. As a result, substantial rewriting efforts have been taking place to accommodate these needs. Motivation for the rewrite process was found in the perspective to support more of these creative endeavours that would be hard to undertake in more closed or less configurable live coding systems.

Conclusion

Current issues and shortcomings

Sardine is both a new software/environment and an architecture model for a Python based live coding library. Developed only by a small dedicated team of developers with a few enthusiastic users, some features are already hard to keep a track of, for they are not often used. Much needed updates are currently delayed to address more urgent concerns. Though tests have recently been introduced, only time-critical parts of the system are currently extensively covered. The v.0.2.0 rewrite focused on improvements to the temporal model and component handling, and although it's promising, it remains fragile and requires careful, time-consuming testing to identify regressions and new issues brought by additions to the model. As such, regular updates are being released to increase the robustness and playability of the system. Now at version v.0.3.0 -at the time of last revision of this paper -, Sardine has started to stabilise around its core features. Sardine is in need of documentation focused on new and inexperienced users. The installation process still requires the installation of a Python runtime and some knowledge of handling Python packages, a task which might not be easy for non programmers. Existing text-based documentation provides a thorough tour and exploration of the system, but lacks friendly videos, tutorials and content that could boost Sardine's adoption curve. As a temporary solution to this, we show code used by performers in a special Showcase section of the website. The internal editor, already fully functional, has yet to become a gate of entry as an easy playground for new users. As a result of its design and multi-paradigm nature, Sardine is also a system that can prove to be hard to learn (and teach) depending on previous user experience with live coding interfaces and programming proficiency.

The source code is extensively documented for contributors but most of the internal documentation is currently invisible to end-users. Sardine's modularity can be considered as both its major strength and weakness. Initial configuration of Sardine can be quite intimidating for newcomers that may not know what they wish to accomplish with it, especially regarding external IO (MIDI, OSC, SuperDirt, etc.). Most options are disabled by default and must be added manually to configure a session. The reliance of loosely coupled software components (Web editor, Fishery, Sardine, SuperCollider, etc) can prove helpful for veteran live coders that are familiar with such architectures but confusing for new users.

Learning, contributing, testing

The Sardine project is freely usable and modifiable by its users. It is currently hosted on GitHub under the GNU General Public License v.3.0 ("GNU General Public License, Version 3" 2007). We warmly encourage anyone interested to try this experimental system and help report and triage bugs, and collectively build this new live coding system. Sardine is currently by no means as complete or stable as other existing propositions used by the community. We are still in the process of catching up with the quality and featurefulness of known alternatives, thinking as Sardine as a way to extend or collaborate with other musicians and visualists using different live coding environments.

it to a wider audience these few last months, in particular the Cookie Collective, Rémi Georges, Yassin Siouda and many more from the online Sardine chat channels.

Figure 1 :

 1 Figure 1: Sardine first algorave in Lorient (France), 2022, October 13th. Photography: Guillaume Kerjean.

Figure 2 :

 2 Figure 2: Architecture diagram of the customised asynchronous event loop. ³Integrated Development Environment. ⁴The process for setting up various interfaces is extensively detailed on Sardine's website.

Figure 3 :

 3 Figure 3: Lifetime of an asynchronous 'swimming function'.

Figure 4 :

 4 Figure 4: A commented complete example of a 'swimming' recursive function.

  Figure 5: A 'surfboard', custom FoxDot-like emulation adding a new playing mode to Sardine.

Figure 6 :

 6 Figure 6: Usage of the 'sleep' method to defer execution, mimicking Sonic Pi.

Figure 6 :

 6 Figure 6: Excerpt from Sardine boot process, addition of a MIDI Output.

Figure 7 :

 7 Figure 7: Sending MIDI using multiple components of the bowl.

Figure 8 :

 8 Figure 8: Several calls to the parser spread in different Sardine methods

Figure 9 :

 9 Figure 9: Various commented examples of Sardine patterns.

  ⁷CLCE: https://github.com/sletz/CLCE. GRAME National center for Music Creation based in Lyon, France (https://www.grame.fr/).

Figure 11 :

 11 Figure 11: Sardine v0.1.0 code during an algorave (Zorba, Paris), November 3rd, 2022

Figure 12 :

 12 Figure 12: Tölvera artificial life simulation run inside a Sardine '@swim' function.

  Jack Armitage: my work is supported by the Intelligent Instruments project (INTENT), which is funded by the European Research Council (ERC) under the European Union's Horizon 2020 research and innovation programme (Grant agreement No. 101001848). I contributed use case examples and testing feedback on Sardine's performance, and assisted with proof reading and final editing of the paper.

¹Some videos of this older system can be found on Raphaël's YouTube channel: https:/youtube.com/watch?v=MHGYtlKibUo

²Read, Eval, Print, Loop: mechanism used by most interpreted languages to process user input from the command line and return textual output.

AcknowledgmentsRaphaël Forment: I warmly thank my thesis supervisors Laurent Pottier and Alain Bonardi for their support and advice in the creation of this tool. I thank the doctoral school 3LA from the University of Lyon for the funding it provided to this research. I extend my thanks to the musicians and friends who allowed me to take Sardine on stage and to present ⁸Python codebase for the Magnetic Resonator Piano: https://github.com/Intelligent-Instruments-Lab/iil-mrp-tools/ ⁹Code from this experiment can be found on the Tölvera GitHub repository: https://github.com/Intelligent-Instruments-Lab/iil-python-tools


Hayrullah Karabulut

Sezim Ezgi Güvercin

James Hollingsworth

Ali Özgün

Long silence on the East Anatolian Fault Zone

published or not. The documents may come

Introduction and Regional Settings

The East Anatolian Fault Zone (EAFZ) is bounded by the Karlıova Basin to the northeast, where the relative motion between Eurasia and Arabia is distributed across the North Anatolian Fault (NAF), EAFZ and the Varto Fault Zone (VFZ) [START_REF] Arpat | The East Anatolian Fault System; Thoughts on its Development[END_REF]Şaroğlu, 1972, Şengör, 1979;[START_REF] Şaroğlu | Doğu Anadolu'nun neotektonik donemde jeolojik ve yapısal evrimi: Istanbul Universitesi[END_REF] (Figure 1). In the southwest, the EAFZ joins the Dead Sea Fault (DSF) at Kahramanmaraş Triple Junction (KMTJ), near Türkoğlu (e.g. [START_REF] Jackson | Active tectonics of the Alpine-Himalayan Belt between western Turkey and Pakistan[END_REF]Hempton, 1987;[START_REF] Muehlberger | Observations on the complexity of the East Anatolian Fault, Turkey[END_REF][START_REF] Barka | Strike-slip fault geometry in Turkey and its influence on earthquake activity[END_REF] and continues towards Cyprus Arc where the convergence is accommodated between the Nubia and Anatolia plates [START_REF] Mckenzie | Active tectonics of the Mediterranean region[END_REF][START_REF] Dewey | Plate tectonics and the evolution of the Alpine system[END_REF] (Figure 1). Geologic and geomorphological studies on the EAFZ indicate that the slip rate systematically decreases from ~10 mm/yr near Karlıova to ~4 mm/yr near Türkoğlu [START_REF] Mahmoud | Kinematic study at the junction of the East Ana-tolian Fault and the dead sea fault from GPS measurements[END_REF]Koç and Kaymakçı, 2013;[START_REF] Bayrak | Earthquake hazard analysis for East Anatolian Fault Zone, Turkey[END_REF][START_REF] Aktuğ | Slip rates and seismic potential on the East Anatolian Fault System using an improved GPS velocity field[END_REF]. Further southwest, the slip rate decreases further down to ~2.5 mm/yr on the main fault and to ~1 mm/yr on sub-parallel faults [START_REF] Herece | Doğu Anadolu Fayı (DAF) Atlası, general directorate of mineral research and exploration[END_REF][START_REF] Duman | The East Anatolian Fault: geometry, segmentation and jog characteristics[END_REF][START_REF] De Gülerce | Probabilistic seismic-hazard assessment for East Anatolian Fault zone using planar fault source models[END_REF][START_REF] Yönlü | Geological and geomorphological evidence for the southwestern extension of the East Anatolian Fault Zone, Turkey[END_REF].

As a consequence of the geometry and the plate motion direction as well as significant slip rate variations, the EAFZ holds significant geometric complexities over its ~600 km length displaying seismicity patterns with gaps, localized clusters and sections of diffuse activity (Güvercin et al., 2022 and references therein). The geometric complexities along the fault are frequently used as a basis for fault segmentation associated with the rupture extents of the historical earthquakes [START_REF] Duman | The East Anatolian Fault: geometry, segmentation and jog characteristics[END_REF].

From northeast to southwest, the various segments experienced moderate size earthquakes since 2010; The Kovancılar earthquake in 2010 broke ~30 km of the northeasternmost extent of the EAFZ [START_REF] Tan | Aftershock study and seismotectonic implications of the 8 March 2010 Kovancılar (Elazığ, Turkey) earthquake (MW = 6.1)[END_REF]. The adjacent Palu segment (~80 km long) partially ruptured between 2010-2011 with two moderate earthquakes (Mw5.4 and Mw6.1) and has been continuously active [START_REF] Duman | The East Anatolian Fault: geometry, segmentation and jog characteristics[END_REF]Nalbant et al. 2002;Kondorsskaya and Ulomow, 1999).

since with small magnitude seismicity [START_REF] Güvercin | Active Seismotectonics of the East Anatolian Fault[END_REF]. The 2020 Mw6.8 Sivrice earthquake, which ruptured ~45 km of the EAFZ from Palu to Pütürge was the largest on the EAFZ during the instrumental period until the 2023 earthquakes [START_REF] Konca | From interseismic deformation with near-repeating earthquakes to co-seismic rupture: a unified view of the 2020 Mw6.8 Sivrice (Elazığ) Eastern Turkey Earthquake[END_REF]. From the southwest of the Pütürge segment to the end of Amanos, the observations indicate low strain rate, low seismicity rate and long recurrence times of large historical earthquakes. Two largest magnitude earthquakes in 2023 occurred on a known seismic gap which did not experience similar large rupture since the historical earthquake of 1114 M>7 (Ambraseys, 1988[START_REF] Ambraseys | Earthquakes in the Mediterranean and Middle East: A Multidisciplinary Study of Seismicity upt o 1900[END_REF]. We present seismic activity before the seismic crisis, new observations from two largest events, rupture geometries and near-field ground displacements from sub-pixel correlation of satellite images, aftershock distributions and focal mechanism solutions, and a reevaluation of the seismic potential of the ruptured fault based on the large Mw7.8 earthquake on the EAF.

Data and Methods

We relocated three mainshocks and the aftershocks using the event database reported by Disaster and Emergency Management Authority (AFAD: https://tdvms.afad.gov.tr). The seismic waveform data from broadband and acceleration stations are used to revise and append the catalog to improve the locations of the aftershocks with Mw5.0+. The magnitudes reported by AFAD are used in the revised catalog. The completeness of the catalog provided by AFAD was high (Mc=3.8) and requires more detailed analysis to better characterize the seismic crisis. Here we only concentrate on the two largest events and big aftershocks during the first 2 weeks of the seismic activity. A new local velocity model with the station corrections was computed with the joint use of the aftershock data from the 2023 earthquakes and the catalog from the previous time period between 2012and 2020(See Güvercin et al., (2020) for the catalog and the computational details). The spatio-temporal evolution of the relocated seismicity is displayed in the Supplementary Material (Figure S1a) with the locations and uncertainties of the largest events (Figure S1b). We used the focal mechanism solutions from the reports of various agencies (GCMT, GFZ, INGV, KOERI, AFAD, OCA, USGS) (Table S1) and obtained unreported mechanisms of M3.0+ events during the preseismic period from the regional waveforms using the Cut-and-Paste method (Table S2) (gCAP) [START_REF] Zhao | Source estimation from broadband regional seismograms[END_REF][START_REF] Zhu | Advancement in source estimation techniques using broadband regional seismograms[END_REF][START_REF] Zhu | Parametrization of general seismic po-tency and moment tensors for source inversion of seismic waveform data[END_REF].

A total of 49 focal mechanism solutions are displayed in Figure 6, of which 9 of them are computed here. The computational details for the relocation and the determination of source mechanisms are the same as [START_REF] Güvercin | Active Seismotectonics of the East Anatolian Fault[END_REF].

Coulomb stress analysis is performed for specified faults (Toda et al. 2011;[START_REF] Lin | Stress triggering in thrust and subduction earthquakes, and stress interaction between the southern San Andreas and nearby thrust and strike-slip faults[END_REF] using the geometry of the EAFZ from [START_REF] Güvercin | Active Seismotectonics of the East Anatolian Fault[END_REF] while the receiver faults with no slip are adopted from the active fault map of the Turkish General Directorate of Mineral Research and Exploration (MTA) [START_REF] Emre | Active Fault Map of Turkey with an Explanatory Text[END_REF]. The slip model for the Coulomb model the finite-fault model of USGS (2023), which is obtained from the teleseismic, and strong-motion and high-rate GNSS waveforms and GNSS static (https://earthquake.usgs.gov/earthquakes/eventpage/us6000jllz/finite-fault). We obtain the source-time functions of the two events by employing a preliminary finite fault model using teleseismic P and SH waves [START_REF] Ji | Source description of the 1999Hector Mine, California Earthquake, Part I: wavelet domain inversiontheory and resolution analysis[END_REF].

The 2D component of the surface deformation field is retrieved by correlation of pre and post event Sentinel-2 optical satellite images. The short time period between the two post-event images, which are both within 3 and 8 days of the earthquake sequence, means they likely contain relatively little post-seismic deformation, and any difference in displacement between the two images will be far below the threshold of the correlation technique (~1/10th pixel). We correlate band 8 of the Sentinel-2 scenes, which in this case gives the cleanest displacement fields, using the frequencybased correlator from the COSI-Corr software package [START_REF] Leprince | Automatic, precise, ortho-rectification and co-registration for satellite image correlation, application to seismotectonics[END_REF]. Additional details on the processing and denoising are presented in the Supplementary Material (Figure S2).

Seismic Activity Before the 2023 Ruptures

Figure 2 shows the seismicity evolution along the EAFZ from Jan 2012 to 06 Feb 2023 with magnitudes M2.5+. Overall, the seismicity rate is high from the Karlıova Trrple Junctron (KOTJ)

to the western termination of the 2020 Mw6.8 Sivrice rupture. From the east of the Pütürge segment to the end of the Amanos, the seismicity rate is nearly uniform except for longitudes between 37.0-37.5 where the Karasu Valley elongates nearly in N-S direction and merges to the EAFZ (Figure 1,2). In this zone, a moderate size earthquake (Mw5.1) occurred on the Narlı Fault Zone in 2012. Other clusters towards the NE of Narlı Fault Zone and on the Pazarcık segment have been continuously active producing episodic seismic activities (Figure S1). Notable seismic activity is observed, between 2012-2023, on the Yesemek Fault with earthquakes M3.0+, subparallel to the Amanos segment (Figure 2). The focal mechanism solutions of these earthquakes show dominant normal fault solutions with some left-lateral strike-slip component (Figure 2). An increase in the seismicity rate is observed within these clusters in 2022 (Figure 2a, yellow and gray shaded boxes, Figure 2b, yellow and gray shaded bars). The rupture lengths of the large earthquakes during the historical and instrumental periods are shown on the bottom with different colors.

Co-seismic Ruptures and Surface Offsets

Sub-pixel correlation of Sentinel-2 optical satellite images reveals the horizontal component of motion produced during the seismic sequence. The EW and NS components of the displacement field are consistent with left-lateral strike-slip motion (Figure 3a). The fault traces are clearly expressed as sharp discontinuities in the horizontal displacement field, which reveal a ~300 km and ~140 km surface rupture for the Mw7.8 and Mw7.6 earthquakes, respectively.

For the Mw7.8 earthquake highest surface slips are observed on the EAFZ between KMTJ and Sivrice segment between latitudes 37.5° and 38° with a peak value of 7 m (Fig. 3c). The rupture initiation along the Narlı fault, which corresponds to an Mw7.0 sub-event, is also observed as a change in the gradients on both components along a 30 km length (white box in Figure 3a, and In the east, a splay of ~10 km length trending east from the main rupture zone near the junction of Sürgü Fault, is observed (Figure 3a-b). When the rupture arrived near the Pütürge segment to the northeast, it deviated towards the east and terminated after propagating another 20 km. The optical correlation results show that the rupture stopped in the northeast, near the termination of the 2020

Mw6.8 Sivrice earthquake at Çelikhan, and at the Hatay graben at the southwestern end.

The rupture of the Mw7.6 event is also clearly expressed in the EW component, with decreasing magnitude to the east (Figure 3a). The surface rupture can be traced for a total of ~140 km, with left-lateral strike slip surface displacements of up to 8.5 m (Fig. 3d). The offset values decrease dramatically where the rupture zone orientation changes from E-W to SW-NE; slip values then rapidly taper for ~45 km between longitudes 37.5° and 38.0°. It is clear from the surface offsets that the rupture continued toward the NE, subparallel to the EAFZ, rather than continuing toward the Sürgü Fault in the east. At the western limit, the rupture continued towards the SW and ended quite abruptly near Göksun. Both events show predominantly left-lateral displacements.

Correlation bias, resulting from differencing reflectance conditions between the pre-and postimages (e.g. fresh snowfall in the post-image) adds a spatially correlated noise signal to the NS component, which currently limits our ability to resolve the fault-normal component of deformation (which is smaller in magnitude than the strike-slip component, and thus has a lower signal-to-noise ratio). Furthermore, we note that the left-lateral displacement estimates along the central section of the Mw 7.8 rupture may be slightly corrupted by the noisy NS component, given the NE-SW fault strike at this location (between 37.1° and 37.5°E). producing any surface offsets. The comparison shows that the first 20 s of the Mw7.8 event featured relatively moderate moment release rates, with a magnitude of ~7.0. This is consistent with the initial phase of the rupture with a relatively small ~Mw7.0 subevent which broke ~50 km long fault to the north before reaching to the EAFZ.

The accurate relocation of the Mw7.0 sub-event with horizontal uncertainties less than ~1km

clearly shows that the rupture initiated on the subparallel Narlı fault not on the EAF (Figure S1b).

The distance between the epicenter of the Mw7.0 sub-event and the EAF is about 20 km. The correlation of optical images also shows surface slip on the Narlı Fault where the hypocenter is located (Figure 4). These observations confirm that the Mw7.8 earthquake was the continuation of the Mw7.0 event initiated by the arrival of the rupture front to the EAFZ, ~20 s later (Fig. 4 and5). The moment release increased significantly with a bilateral rupture propagation along the EAFZ. The complexity of the source time function is a consequence of the rupture of different segments along the EAFZ, while moment release in all the patches are quite significant with a total duration exceeding 80 s. The Mw7.6 Çardak earthquake on the other hand, has a duration of 40 s, and a very sharp onset consistent with the very high surface offsets observed in the epicenter area (Fig. 34). [START_REF] Vallee | A new database of source time functions (STFs) extracted from the SCARDEC method[END_REF]. The other source time functions are from finitefault studies [START_REF] Konca | The 2014, MW6.9 North Aegean earthquake: Seismic and geodetic evidence for coseismic slip on persistent asperities[END_REF][START_REF] Konca | From interseismic deformation with near-repeating earthquakes to co-seismic rupture: a unified view of the 2020 Mw6.8 Sivrice (Elazığ) Eastern Turkey Earthquake[END_REF].

The Aftershock Distribution and Earthquake Source Mechanisms

Figure 6 shows the mainshocks and the aftershock distribution between 06th Feb 2023 and 20th

Feb 2023. The colours are used to illustrate the time evolution of the aftershock activity. The early aftershocks clearly illuminate the rupture zone and are spatially consistent with the surface rupture obtained from image correlation (Figure S3 in Supplementary Material).

The location of the initial ~Mw7.0 subevent is precisely determined from the seismic stations in the near proximity of the epicentral region (Supplementary Material, Figure S1b). A small precursory event was also identified at several stations prior to the Mw7.0 event. The focal mechanism solution of the Mw7.0 subevent determined from the moment tensor inversion of the first 20 s of the near-field waveforms reveals a mechanism with a NE-SW left-lateral strike-slip motion with a normal component (Figure5, ID: 1a, Figure S4). It is worthwhile to mention the similarity of the source mechanisms of Mw7.0, 2023 and Mw5.1, 2012 (Figure 2, ID:1). The epicenters of both events hint that the same fault zone experienced ruptures of different lengths.

The Mw7.8 earthquake occurred ~20 s later with the arrival of the rupture front of the ~Mw7.0 subevent at the EAFZ, (the epicenter is marked as the intersection of the Mw7.0 rupture with the EAFZ). The largest aftershock with Mw6.6 occurred ~10 min later on the Amanos segment close to the epicenter of the Mw7.0 event.

The background seismicity was low during the interseismic period on the Çardak Fault (Figure 2).

The only reported historical event on the Çardak Fault is the 1544, M6.8 earthquake (Kondorskaya and Ulomow, 1999). No notable pre-seismic activity is detected at the epicentral region of the Mw7.6 earthquake following the rupture of the Mw7.8 event. The most notable feature on the Mw7.6 rupture is the evolution of the seismic cluster at the western termination at the Göksun Bend having a continuation to the Savrun Fault. The aftershocks and the source mechanism solutions in this cluster show two distinct orientations nearly perpendicular to each other. The first cluster to the west of the rupture area has NE-SW orientations with strike slip geometry, consistent with the surface rupture of the Mw7.6 with the orientation of the Savrun fault ( Figure 6, ID:3, 25, 30, 34). The second cluster nearly perpendicular to the first displays normal mechanism solutions (Figure 6, ID: 6, 14, 17, 39).

On 20 Feb 2023, 2 weeks after the Mw7.8 and Mw7.6 earthquakes, a third earthquake with Mw6.4 occurred on the Hatay fault, which bounds the city to the southeast. The aftershock activity was focused on the northwest of the mapped fault consistent with the source mechanism solutions (Figure 6, ID: 16-37), showing a NW-SE oriented normal fault (Figure 6, ID: 16-37). The offshore continuation of the activity in the İskenderun Bay is observed along the fault which extends to the Cyprus Arc in the Mediterranean Sea (Figure 1 and Figure 6). In the NE termination of the Mw7.8 rupture, the aftershock activity stops soon after the SW termination of 2020 Mw6.7 Sivrice rupture.

These results suggest that the Mw7.8 rupture possibly stopped due to the change of the kinematics from strike-slip to normal, but then the stress loading due to this large earthquake was sufficient to rupture the Hatay Fault (Figure 7c inset, Figure S5).

Toward the northeast of the rupture area, a progressive increase of seismic activity within two weeks following the two large ruptures, extended to the Palu segment and finally reached to KOTJ in the northeastern continuation of the EAFZ. Two large earthquakes created a large zone of stress perturbations far from the ruptured faults, triggering moderate size earthquakes of Mw5.0+ and clusters of seismicity (Figure 6). Two Mw5.0+ events in the Malatya and Bingöl are just few examples of distant triggering. The majority of these triggered events occurred within the previously observed long lasting seismic clusters [START_REF] Karabulut | Synchronization of small-scale seismic clusters reveals large-scale plate deformation[END_REF] and are mostly associated with the geometric complexities such as bends and stepovers of the hosting faults, which are also prone to generate moderate size earthquakes. The absence of aftershocks between the eastern end of the Mw7.6 rupture at the Sürgü Fault and the EAFZ confirm the termination of coseismic rupture before reaching the EAFZ. However, the continuation of the seismicity to the northeast with a subparallel orientation to the Doğanşehir Fault (Figure 1) is well characterized by the lineament of the aftershocks and the focal mechanism solutions of Mw5.0+ earthquakes (Figure 6,ID: 4,24,26,28,36). S1. The inset figure shows the zoom of the seismic activity (black square) in the epicentral region of the Mw7.0 earthquake.

Coulomb Stress Analysis

To illustrate the interactions between the two largest earthquakes, and how these large earthquakes may control the distribution of the aftershocks at the terminations of two ruptures, as well as the neighboring faults, we modelled the changes in Coulomb failure stress.

Figure 7a shows the Coulomb stress changes on receiver faults following the Mw7.8 earthquake.

The stress increase is observed at the future epicenter of the Mw7.6 earthquake on the Çardak Fault by ~1.0bar. The southern termination of the rupture experiences a stress increase along the DSF and the Hatay fault which ruptured with a Mw6.4 earthquake two weeks later. Figure S5 shows the Coulomb stress change (~1bar) on the Hatay Fault which was built as a receiver fault with the mechanism of the Mw6.4 event (Table S1). The NE continuation of the EAFZ also experienced an increase in Coulomb stress along the Pütürge and Palu segments.

Figure 7b shows the distribution of the aftershocks during the first 9 hours before the Mw7.6 event with the Coulomb stress changes calculated at a depth of 10 km using the geometry of the Çardak Fault as specified faults. The distribution of the early aftershocks is consistent with the areas of Coulomb stress increases.

Discussion

The initiation and triggering of two large earthquakes and the rapid growth of the aftershock zone together with the increased level of pre-seismic activity are indications that the ruptured segments of the EAFZ were at a state of critical stress. The stress on the fault within a low strain rate environment may stay long near failure during the late stages of interseismic period. The rupture would start at any location on the fault that is in a critical state, and thus sensitive to stress perturbations. The epicenter of the Mw7.8 earthquake was far from the southwestern termination of the 2020 Mw6.9 Sivrice earthquake where the Coulomb stress increased, but instead occurred at a splay fault in Narlı, where high seismic activity rate within a seismic cluster is observed with some temporal accelerations. The focal mechanism solutions of the earthquakes in this cluster show dominant normal faulting with largest, 2012 Mw5.1 event. The surface offsets on this segment also shows significant extension. Therefore, we conclude that the earthquake initiated within a zone of continuous seismic activity with the occurrence of Mw5.1 earthquake in 2012 and progressively weakened resulting in the rupture of the Mw7.0 subevent. Similar observations were made for the Mw7.4 1999 Izmit earthquake [START_REF] Bouchon | Observation of rapid long-range seismic bursts in the Japan Trench subduction leading to the nucleation of the Tohoku earthquake[END_REF].

Did a Permanent Barrier Stop the 2020 Sivrice and 2023 Kahramanmaras Earthquakes?

The termination of the 2023 Mw7.8 earthquake in the east and the termination of the 2020 Mw6.8

Sivrice earthquake in the west share a similar zone where several faults converge to the EAFZ.

Correlation of optical images before and after the earthquake also reveals that the Mw7.8 event deviated from the main fault plane and terminated by rupturing a fault to the east.

The Sürgü fault, situated to the west of this junction, did not break in either of the two large earthquakes of 2023. The Mw7.6 earthquake started toward on the Çardak fault, which is to the west of the Sürgü fault, but could not penetrate through the Sürgü fault, instead deviating to the northeast. Despite being mapped as an active fault [START_REF] Emre | Active Fault Map of Turkey with an Explanatory Text[END_REF] and previously reported as seismically active (e.g., [START_REF] Güvercin | Active Seismotectonics of the East Anatolian Fault[END_REF], a clear gap is apparent in the aftershock activities of the Mw7.8 rupture and the continuation of the Mw7.6 event along the Sürgü Fault. The Coulomb stress analysis shows that the Mw7.8 event loaded the Çardak Fault, but led to a stress decrease along the Sürgü fault. The lack of slip or the aftershocks along the Sürgü fault may be related to the stress shadow induced by the Mw7.8 event.

The distribution of seismicity with respect to the ruptures indicates that the dip direction of the EAF may also change around this junction. The detailed study of the aftershock activity of the Sivrice earthquake indicates a northwest dipping fault [START_REF] Güvercin | Active Seismotectonics of the East Anatolian Fault[END_REF]. The aftershocks of the Mw7.8 earthquake are located along the southwest of the surface fault trace indicating a southwest dip in accordance with the focal mechanisms in this zone. Thus, we conclude that the change in the fault dip may also had an influence of the rupture termination.

These observations imply that the zone where Sürgü, the EAFZ and the faults related to the Bitlis Suture to the east comprise a persistent barrier to the ruptures in both directions. The rupture termination of the 2020 Mw6.8 Sivrice and the 2023 Mw7.8 earthquakes may reflect the impact of this boundary, which led to the clamping of the nearby Sürgü fault.

Synchronization of two largest earthquakes

The geodetic and geologic slip rates of the Cardak Fault are estimated between 1.5-2.5 mm/yr, much lower than the rates for the Amanos Fault (~4.6 mm/yr ) and the East Anatolian Fault (~6.8 mm/yr) (Reilinger et al. 2006;[START_REF] Duman | The East Anatolian Fault: geometry, segmentation and jog characteristics[END_REF]Koç and Kaymakçı, 2013). Furthermore, the historical earthquakes on this fault are not well known. It is anticipated that the recurrence time for an earthquake as large as Mw7.6 on the Çardak Fault is much longer than those on the EAF.

The stress perturbations and seismic activation at regional scale

The two largest earthquakes of 2023 ruptured more than 500 km of the EAFZ. A large aftershock zone and its progressive expansion to surroundings imply the region is susceptible to the activation of swarm type seismicity and moderate size earthquakes. The loading of the fault at various scales during the post-seismic period and the relaxation of the lower crust will load the fault far from the ruptured faults. Many of the faults, e.g., Malatya, Tuz Gölü, Ecemiş, display low strain rates and long recurrence times. Therefore, it is not predictable how loading will influence timing of the rupture on these faults. In the meantime, the activity following the East Anatolian Doublets on these faults with Mw5.0+ indicate these faults may have been loading (e.g., Mw5.1 Malatya, Mw5.1 Erciyes, Mw5.3 Konya) (Figure S6). On the western termination of the Mw7.6 rupture at Göksun Bend, the aftershock activity follows the orientation of the Savrun Fault, with NE-SW trend towards the Adana Basin. In the Adana Basin, the seismicity is diffuse, normal and strike slip faulting are related to the Missis-Kyrenia Fault zone on the north of Cyprus.

2013 Earthquake Cycle, Moment Deficit and Inferences on Seismic Hazard Assessment

In order to assess the seismic hazard in a region, it is crucial to combine information on the historical earthquakes, paleoseismic studies, geodetic measurements of crustal deformation and instrumental earthquake observations. We attempt to assess the seismic potential of this large event on the EAFZ by considering geodetic slip rates and the Gutenberg-Richter (GR) parameters.

This approach follows [START_REF] Stevens | On the relationship between strain rate and seismicity in the India-Asia collision zone: implications for proba-bilistic seismic hazard[END_REF] and was applied to the EAFZ for each segment separately by [START_REF] Güvercin | Active Seismotectonics of the East Anatolian Fault[END_REF] where the seismic strain accumulation around the fault is equated to the moment release by earthquakes following the GR law. The updated approach yields both the maximum expected magnitude (Mmax) (1) and a return period (Tr) (2) by extending the whole ruptured section of the EAFZ in light of the recent earthquake.

𝑀 !"# = $ % & ' () #𝑙𝑜𝑔 $* #1 - &) % ) + 𝑙𝑜𝑔 $* (𝛼. 𝑚̇) -9 -𝑎) (1)
Even if we consider the cumulative build up since the great 1114 event, the suggested magnitudes of the 18 th and 19 th century earthquakes still release significant moment leading to insufficient moment accumulation. The results indicate that the magnitudes of these historical earthquakes are likely to be overestimated by ~0.4 or some of them may not have actually ruptured the EAFZ. The Table 1: Geodetic and seismologic parameters on the 2023 events along DAF. NOE: Number of events. Geodetic strain accumulation rates are from Reilinger et al., (2006). The second row with * is obtained from the rate of the earthquakes with Mc=2.5 per year between 2007-2019 [START_REF] Güvercin | Active Seismotectonics of the East Anatolian Fault[END_REF]. Table 2: Seismic potential of the EAFZ for 3 scenarios using geodetic strain rates from Weiss et al. ( 2020).

Conclusions

The 2023 earthquakes are an important reminder that moment accumulation since the previous large earthquake, and simple assumptions of earthquake recurrence rates are too simplistic to understand the extreme events. A section of a fault zone that releases moment partially in one seismic cycle, can break with much larger moment in the next cycle depending on the stress conditions along neighboring sections of the fault [START_REF] Konca | Partial rupture of a locked patch of the Sumatra megathrust during the 2007 earthquake sequence[END_REF][START_REF] Kaneko | Towards inferring earthquake patterns from geodetic observations of interseismic coupling[END_REF]. The surface rupture and the moment rate function of the 2023 Mw7.8 earthquake shows that this event ruptured multiple segments, and that stress on the fault was high for a significant portion of the EAFZ. The rupture was stopped at a clear barrier in the northeast where three faults connect together and the fault dip direction along the EAFZ changes. The Mw7.8 earthquake initiated along a subparallel fault where significant long term seismic activity was observed before the mainshock, indicating that the rupture initiated at a location within a zone weakened prior to the rupture. The rupture of the Mw7.6 event 9 hours later significantly reflects the stress changes induced by the previous Mw7.8 mainshock.

The combination of the two events led to a stress increase on several large fault zones in the surrounding region including the eastern Cyprus arc, the Dead Sea Fault and several faults in the east Anatolia. However, two largest 2023 events changed the state of stress of the entire Anatolian plate, with significant internal deformation. Considering that the seismic activity following these two earthquakes extends to further than ~300 km away from the rupture zone, it is possible that the faults in the entire Anatolian plate are prone to generate high level seismic activity and moderate size earthquake for decades during the postseismic and the relaxation of the lower crust.

Figure 1 :

 1 Figure 1: Tectonic setting of the study region with shaded topography (http://dds.cr.usgs.gov/srtm/version2 1/SRTM30/). Faults are shown by thin blue lines (Emre et al. 2013). Black arrows on the fault traces indicate the relative motion of the faults. The fault ruptures of 2010 Mw6.1 Kovancılar; 2011 Mw5.4 Palu; 2020 Mw6.8 Sivrice earthquakes are shaded in pink, blue and red, respectively. Red stars represent the epicenters of the 2023, Kahramanmaraş earthquakes. AB: Amik Basin. SF: Sürgü Fault. KMTJ: Kahramanmaraş Triple junction. KOTJ: Karlıova Triple Junction. HF: Hatay Fault. AF: Amanos Fault. DSF: Dead Sea Fault. NAF: North Anatolian Fault. VFZ: Varto Fault Zone. YsF: Yesemek Fault. NF: Narlı Fault. YF: Yumurtalık Fault. T: Türkoğlu. TF: Toprakkale Fault. GB: Göksun Bend. DF: Possible continuation of the Doğanşehir Fault. Karasu Valley and Karlıova Basin (KB) are shown by striped areas. Dash black box shows the East Anatolian Fault Zone. Inset shows the locations of the historical earthquakes with M>7.0[START_REF] Duman | The East Anatolian Fault: geometry, segmentation and jog characteristics[END_REF] Nalbant et al. 2002; Kondorsskaya and Ulomow, 1999).

Figure 2 :

 2 Figure 2: Time evolution of the seismic activity with M2.2+ on the EAFZ between Jan 2012-06 Feb 2023. a) The annual evolution of seismicity is displayed with colors and scaled with magnitudes. The blue beachballs represent the focal mechanisms of the earthquakes with M3.5+ before 2023 events (Table S2). ID number of each focal mechanisms is given near the beachballs. The segment names are adopted from Duman and Emre (2013). Yellow and gray boxes show the locations of long-term seismic clusters. Red stars indicate the epicenters of the 2023 earthquake sequence. b) Black circles represent the M2.2+ earthquakes scaled by magnitudes. The seismic activities in the yellow and gray boxes of the top figure are shown as shaded regions with the same colors on the time evolution. Red solid line marks the longitude of the 2023, Mw7.0, Narlı event.

Figure

  Figure4a,b). The surface offsets on the Narlı fault reveal significant normal component (Figure4c,d), in accordance with the focal mechanism of the pre-seismicity in the vicinity (Figure2).

Figure 3 :

 3 Figure 3: Image correlation maps showing (a) EW and (b) NS components of displacement from correlation of Sentinel-2 satellite images (see text for details). NF: Narlı Fault. SF: Sürgü Fault. White box shows the location of Figure 4. Yellow lines show the fault ruptures from both Mw7.8 and Mw 7.6 earthquakes. Along-strike left-lateral displacement profiles are shown for the (c) Mw7.8 (orange line), and (d) Mw7.6 (blue line) events. Various displacement profiles for ruptured sub-segments in (c) are also shown with different colours. Upper left insets in (c) and (d) show the rupture for each case in map view. Decreases in the localized slip generally correlate with geometric complexities.

Figure 4 :

 4 Figure 4: Image Correlation maps showing the (a) EW and (b) NS components of displacement for the region covering the Narlı Fault, which is the epicentral area of the ruptured Mw7.0 subevent, which initiated slip on the larger Mw7.8 rupture. The color scale is modified to enhance the slip discontinuity on the Narlı Fault. Yellow lines highlight surface ruptures. Blue boxes show the

Figure 5

 5 Figure 5 shows the source time functions of the two mainshocks using teleseismic P and SH waveforms from a preliminary finite fault model in comparison to the several strike-slip earthquakes that occurred along the NAF and the EAFZ in the last 3 decades. The 1999 Mw7.4 İzmit and Mw7.1 Düzce earthquakes ruptured the northwest of the North Anatolian Fault with supershear rupture speeds and have very compact source time functions. The 2014 Gökçeada-Samothraki earthquake occurred along the North Aegean Trough with a very slow rupture velocity (~1.5 km/s) and generated an unusually long rupture length of ~90 km. The 2020 Sivrice earthquake ruptured ~45 km length of the adjacent northeast segment of the 2023 rupture without

Figure 5 :

 5 Figure 5: Comparison of source time functions for the 2013 Mw7.8 and Mw7.6 earthquakes along-side other recent strike-slip earthquakes on the NAF and the EAFZ. The source time functions of the 1999 Mw7.4 Izmit and Mw7.15 Düzce earthquakes are from the SCARDEC source time database[START_REF] Vallee | A new database of source time functions (STFs) extracted from the SCARDEC method[END_REF]. The other source time functions are from finitefault studies[START_REF] Konca | The 2014, MW6.9 North Aegean earthquake: Seismic and geodetic evidence for coseismic slip on persistent asperities[END_REF][START_REF] Konca | From interseismic deformation with near-repeating earthquakes to co-seismic rupture: a unified view of the 2020 Mw6.8 Sivrice (Elazığ) Eastern Turkey Earthquake[END_REF].

Figure 6 :

 6 Figure 6: Map view of the aftershock locations during the first 15 days of seismic activity between February 6 th and 21 th . The colours show the time evolution of the aftershocks scaled by magnitudes. The beachballs show the source mechanism solutions of the aftershocks with the numbers on the top and listed in TableS1. The inset figure shows the zoom of the seismic activity (black square) in the epicentral region of the Mw7.0 earthquake.

Figure

  Figure 7c illustrates the Coulomb stress analysis following the ruptures of Mw7.6 and Mw7.8 earthquakes. The cumulative stress transfer of both ruptures observed on the Sivrice and Palu segments reaches to ~1 bar. The Malatya fault experienced stress increase up to ~1.5 bar due to the Mw7.6 Çardak earthquake.

Figure

  Figure7dshows the distribution of the aftershocks during the first 2 weeks after the 6 th February, and the cumulative Coulomb stress change at 10 km depth using the geometry of the EAFZ as specified faults. The distribution of the aftershocks is well correlated with the cumulative Coulomb stress increase, especially along the northern segments of EAFZ and Malatya fault (Figure7d). A large increase in Coulomb stress (~1.5 bars) is observed at the western termination of the ÇF with a highly active zone of aftershock seismicity.

Figure 7 :

 7 Figure 7: Coulomb stress analysis due to 2023 earthquake sequences. a) Coulomb stress changes on receiver faults following the Mw7.8 earthquake. Yellow stars show the locations of the Mw7.0 and Mw7.8 events. b) Coulomb stress changes at 10 km depth after the Mw7.8 earthquake calculated using specified faults with average geometry of the Çardak Fault. Green circles represent the epicenters of the aftershocks during first 9 hours before the Mw7.6 Çardak earthquake. c) Cumulative Coulomb stress changes on receiver faults after two large earthquakes. Inset: Stress changes on the Hatay Fault and Cyprus Arc. Yellow star represents the 20 Feb. 2023 Mw6.4 Hatay earthquake. d) Cumulative Coulomb stress changes at 10 km depth after two large earthquakes calculated using specified faults with average geometry of the EAFZ. Green circles show the epicenters of the aftershocks during the first 15 days of the activity and scaled by magnitude. Faults are shown by pink and black lines[START_REF] Emre | Active Fault Map of Turkey with an Explanatory Text[END_REF].

  The synchronization of two largest 2023 earthquakes (~9 hours apart) implies significant time advance of the Mw7.6 rupture due to the loading or the unclamping of the Çardak Fault as a result of the Mw7.8 rupture. The Coulomb stress increase due to the Mw7.8 rupture with a significant change in the orientation of the NE striking EAF and NNE striking Amanos faults around KMTJ may have further facilitated the rupture of the Çardak Fault. The cumulative motion of the two faults together is consistent with the counterclockwise motion of Anatolia.

  analysis of Mmax and Tr show that the uncertainties in the magnitudes of the historical earthquakes bias the estimates in terms of length and time. Therefore, the last reported historical earthquakes cannot be representative of a complete earthquake cycle. The observations are not sufficient for the Çardak Fault to perform similar analysis to determine Mmax and Tr.

Figure 8 :

 8 Figure 8: The seismic potential of the EAFZ estimated from geodetic strain rates. a) The Gutenberg-Richter relationship computed for rupture area of the 2023 earthquakes. The orange, light blue and green circles represent the long-term catalogs in S1, S2 and S3 respectively. Dark blue circles represent the short-term catalog between 2007-2022. The orange, light blue and green solid lines represent the GR fits of the merged catalog with historical events for S1, S2 and S3, respectively. The a and b values of the GR are shown at the upper right corner. b) The comparison of the cumulative moment build-up (CMB) and the cumulative moment release (CMR) for three different scenarios.

Acknowledgments

We would like to thank to the Disaster and Emergency Management Authority (AFAD) for making available the seismic waveform data and the earthquake catalogs extensively used in this study (https://tdvms.afad.gov.tr/). The waveform data are also used from the seismic stations operated by Bogazici University, Kandilli Observatory and Earthquake Research Institute, Regional Earthquake-Tsunami Monitoring Center (https://doi.org/10.7914/SN/KO). We would like to thank to Professor James Jackson, an anonymous reviewer and the editor, Dr. Yıldırım Dilek, for their comments which significantly improved the manuscript. We benefitted from the discussions with Esen Arpat, Celal Şengör and Professor Dan McKenzie during his visit at Kandilli Observatory.

where 𝛼 is a constant related to the interseismic coupling and 𝑚̇ is the moment build-up rate.

𝑇 + = 1 10 ("()-!"# ) ⁄ (2)

Table 1 shows the parameters used in the analysis. The geodetic slip-rates on the ruptured faults are ~1-3 mm/yr along the southwest end and increases to ~10 mm/yr to the northeast. (The details of the method used here is given in the Supplementary Materials.)

Here, we consider several scenarios. The earthquake in 1114 is considered as a event (M7.8)

with similar extent to the 2023 earthquake (Ambraseys 1988). In the explored scenarios, we aim to better define the seismic cycle of the EAF. Considering the earthquakes of February 6 th , 2023 represent the end of the earthquake cycle on the EAFZ, the calculated cumulative moment buildup is expected to be equal to the cumulative moment release.

Figure 8 compares the cumulative geodetic moment build-up (CMB) and the cumulative seismic moment release (CMR) for three scenarios based on historical earthquakes. In the first scenario (S1), we take all the large historical events as reported [START_REF] Duman | The East Anatolian Fault: geometry, segmentation and jog characteristics[END_REF]Ambraseys, 1988), considering moment build up since the 1114 (M7.8) earthquake, and the moment releases of 1893 (M7.0); 1795 (M7.2) and 1782 (M7.1) events (Table 2). In the second scenario (S2) we assume that the accumulated moment has been completely released in the last event cycle by the 1893 (M7.0); 1795 (M7.2) and 1782 (M7.1) events. The corresponding Gutenberg-Richter parameters are given in Figure 8a. In both scenarios, the CMR is one order of magnitude greater than the CMB.

Based on these two scenarios, we developed a third scenario (S3), in which we adjusted the magnitudes of the historical earthquakes to catch up the accumulated moment. The corresponding GR parameters are given in Figure 8a, while Figure 8b shows the cumulative moment release calculated for S1, S2 and S3 with accumulated cumulative geodetic moment built-up. The a-value represents the total seismicity rate of the region while the b-value is used to describe the frequency of the earthquake size distribution. The b values for S1, S2 and S3 are similar (~1) while the a values are 4.6, 4.9, and 5.1 for S1, S2 and S3 respectively.