Compare commits

...

10 Commits

Author SHA1 Message Date
JesseBrault0709
404a3971a3 Add gitea-ci.yml
All checks were successful
Jb-ssg-site CI Pipeline / ci (push) Successful in 2m16s
2024-11-01 22:28:58 -05:00
JesseBrault0709
fc82c1a9ec Add Dockerfile to dockerize build of site. 2024-11-01 18:02:26 -05:00
JesseBrault0709
a25162dfa6 Small styling to ease reading of resume and developer pages. 2024-06-14 17:19:55 +02:00
JesseBrault0709
7fd944d968 Added guest conductor. 2024-06-14 17:12:06 +02:00
JesseBrault0709
eff03cfd43 Added resume and developer pages. 2024-06-14 16:33:20 +02:00
JesseBrault0709
ad55d30a73 Service worker preloading images and pages. 2024-06-14 09:42:27 +02:00
JesseBrault0709
3c63c04a0a Got rid of preloaded images. 2024-06-14 08:37:58 +02:00
JesseBrault0709
6343df000b Deleted Turbo. 2024-06-14 07:38:56 +02:00
JesseBrault0709
4985bb4b86 Using new wvc features. 2024-06-14 07:37:37 +02:00
JesseBrault0709
1f88d1b76d Got rid of Turbo, now just preloading images. 2024-06-14 07:27:48 +02:00
26 changed files with 397 additions and 6710 deletions

View File

@ -0,0 +1,28 @@
name: Jb-ssg-site CI Pipeline
on:
push:
branches:
- main
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout the code.
uses: actions/checkout@v4
- name: Calculate short git commit SHA.
run: echo "SHORT_SHA=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV
- name: Build the Docker image.
run: docker build -t git.jessebrault.com/jessebrault/jb-ssg-site:${{ env.SHORT_SHA }} .
- name: Tag built image as latest.
run: docker tag git.jessebrault.com/jessebrault/jb-ssg-site:${{ env.SHORT_SHA }} git.jessebrault.com/jessebrault/jb-ssg-site:latest
- name: Login to Docker Registry.
uses: docker/login-action@v3
with:
registry: ${{ vars.DOCKER_REGISTRY }}
username: ${{ vars.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
logout: true
- name: Publish short commit SHA image to Docker registry.
run: docker push git.jessebrault.com/jessebrault/jb-ssg-site:${{ env.SHORT_SHA }}
- name: Publish latest image tag to Docker registry.
run: docker push git.jessebrault.com/jessebrault/jb-ssg-site:latest

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM eclipse-temurin:21 AS build
LABEL authors="jessebrault"
WORKDIR /jb-ssg-site/
COPY . .
RUN --mount=type=secret,id=JBARCHIVA_USERNAME,env=JBARCHIVA_USERNAME \
--mount=type=secret,id=JBARCHIVA_PASSWORD,env=JBARCHIVA_PASSWORD \
./gradlew createSsgBinScript && \
bin/ssg build -g && \
./gradlew installServerDist
FROM eclipse-temurin:21
LABEL authors="jessebrault"
WORKDIR /jb-ssg-site
COPY --from=build /jb-ssg-site/dist/ dist/
COPY --from=build /jb-ssg-site/static/ static/
COPY --from=build /jb-ssg-site/build/install/jb-ssg-site-server/ .
EXPOSE 8080
CMD ["bin/JbServer"]

View File

@ -10,18 +10,15 @@ import jakarta.inject.Named
class Header extends BaseWebViewComponent {
final String siteName
final String siteTagLine
final List<Page> menuItems
@Inject
Header(
@Named('siteName') String siteName,
@Global('siteTagLine') String siteTagLine,
@Global('menuItems') List<String> menuItems,
@InjectPages('/*') Set<Page> allPages
) {
this.siteName = siteName
this.siteTagLine = siteTagLine
this.menuItems = menuItems.collect { menuItem ->
allPages.find { it.name == menuItem }
}

View File

@ -3,17 +3,18 @@ package com.jessebrault.site
import com.jessebrault.site.icon.BarsIcon
---
<header id="header" data-turbo-permanent>
<header id="header">
<div class="titles">
<h1>$siteName</h1>
<h2>$siteTagLine</h2>
</div>
<nav>
<button id="nav-bars" class="bars-wrapper">
<BarsIcon />
</button>
<ul id="nav-items">
<Each items={menuItems} transform={<li><a class="nav-link" href={it.path}>$it.name</a></li>} />
<Each items={menuItems} transform={
<li><a class="nav-link" href={it.path}>$it.name</a></li>}
/>
</ul>
</nav>
</header>

View File

@ -9,10 +9,10 @@ import com.jessebrault.site.icon.YoutubeIcon
<body>
<div class="header-banner-container">
<Header />
<% children -> children << banner() %>
<Outlet children={[banner()]} />
</div>
<main>
<% children -> mainChildren.each { children << it } %>
<Outlet children={mainChildren} />
</main>
<footer>
<div class="social-icons">
@ -22,7 +22,6 @@ import com.jessebrault.site.icon.YoutubeIcon
<p>Copyright $copyrightYear Jesse R. Brault. All rights reserved.</p>
</footer>
<script src="/main.js"></script>
<script src="turbo.es2017-esm.js" type="module"></script>
<Each items={pageScripts} transform={<script src={it}></script>} />
</body>
</html>

View File

@ -1,6 +1,9 @@
<div class="banner biography-banner">
<div class="headshot-container">
<img src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait1.jpg" class="headshot" />
<img
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait1.jpg"
class="headshot portrait-photo"
/>
</div>
<div class="banner-text blurb-container">
<p id="jesse-brault-name">Jesse Brault</p>

View File

@ -5,7 +5,10 @@ import com.jessebrault.site.icon.*
---
<div class="banner contact-banner">
<div class="headshot-container">
<img src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait2.jpg" class="headshot" />
<img
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait2.jpg"
class="headshot portrait-photo"
/>
</div>
<article class="banner-text social-media-links">
<h1>Social Media</h1>

View File

@ -2,7 +2,7 @@
<div class="landscape-photo-container">
<img
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/jesse-brault-bratislava.jpg"
class="landscape-photo"
class="headshot landscape-photo"
/>
</div>
<div id="lessons-banner-text" class="banner-text">

View File

@ -2,7 +2,7 @@
<div class="headshot-container">
<img
src="https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait4.jpg"
class="headshot"
class="headshot portrait-photo"
/>
</div>
<div id="videos-banner-text" class="banner-text">

View File

@ -0,0 +1,38 @@
package com.jessebrault.site.developer
import com.jessebrault.site.util.TitleMaker
import com.jessebrault.ssg.di.InjectText
import com.jessebrault.ssg.di.SelfPage
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.page.PageSpec
import com.jessebrault.ssg.text.Text
import com.jessebrault.ssg.view.WvcPageView
import jakarta.inject.Inject
@PageSpec(name = 'Developer', path = '/developer')
class DeveloperPage extends WvcPageView {
final Text developer
final Page selfPage
private final TitleMaker titleMaker
@Inject
DeveloperPage(
@InjectText('/Developer.md') Text developer,
@SelfPage Page selfPage,
TitleMaker titleMaker
) {
this.developer = developer
this.selfPage = selfPage
this.titleMaker = titleMaker
}
String getTitle() {
titleMaker.makeTitle(pageTitle)
}
String getDescription() {
developer.frontMatter.description
}
}

View File

@ -2,7 +2,6 @@ package com.jessebrault.site.lessons
import com.jessebrault.site.util.TitleMaker
import com.jessebrault.ssg.di.InjectText
import com.jessebrault.ssg.di.InjectTexts
import com.jessebrault.ssg.di.SelfPage
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.page.PageSpec
@ -16,22 +15,22 @@ class LessonsPage extends WvcPageView {
static final String description = 'Information about lessons offered by Jesse Brault.'
final Page selfPage
final Text lessons
private final TitleMaker titleMaker
private final Text lessonsText
@Inject
LessonsPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Lessons.md') Text lessonsText) {
LessonsPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Lessons.md') Text lessons) {
this.selfPage = selfPage
this.titleMaker = titleMaker
this.lessonsText = lessonsText
this.lessons = lessons
}
String getPageTitle() {
titleMaker.makeTitle(selfPage.name)
String getTitle() {
titleMaker.makeTitle(pageTitle)
}
String renderLessons() {
this.lessonsText.render()
this.lessons.render()
}
}

View File

@ -0,0 +1,34 @@
package com.jessebrault.site.resume
import com.jessebrault.site.util.TitleMaker
import com.jessebrault.ssg.di.InjectText
import com.jessebrault.ssg.di.SelfPage
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.page.PageSpec
import com.jessebrault.ssg.text.Text
import com.jessebrault.ssg.view.WvcPageView
import jakarta.inject.Inject
@PageSpec(name = 'Résumé', path = '/resume')
class ResumePage extends WvcPageView {
final Text resume
final Page selfPage
private final TitleMaker titleMaker
@Inject
ResumePage(@InjectText('/Resume.md') Text resume, TitleMaker titleMaker, @SelfPage Page selfPage) {
this.resume = resume
this.titleMaker = titleMaker
this.selfPage = selfPage
}
String getTitle() {
titleMaker.makeTitle(pageTitle)
}
String getDescription() {
resume.frontMatter.description
}
}

View File

@ -2,7 +2,6 @@ package com.jessebrault.site.videos
import com.jessebrault.site.util.TitleMaker
import com.jessebrault.ssg.di.InjectText
import com.jessebrault.ssg.di.InjectTexts
import com.jessebrault.ssg.di.SelfPage
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.page.PageSpec
@ -16,22 +15,22 @@ class VideosPage extends WvcPageView {
static final String description = 'Conducting videos of Jesse Brault.'
final Page selfPage
final Text videos
private final TitleMaker titleMaker
private final Text videosText
@Inject
VideosPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Videos.md') Text videosText) {
VideosPage(@SelfPage Page selfPage, TitleMaker titleMaker, @InjectText('/Videos.md') Text videos) {
this.selfPage = selfPage
this.titleMaker = titleMaker
this.videosText = videosText
this.videos = videos
}
String getPageTitle() {
titleMaker.makeTitle(selfPage.name)
String getTitle() {
titleMaker.makeTitle(pageTitle)
}
String renderVideos() {
videosText.render()
videos.render()
}
}

View File

@ -18,7 +18,7 @@ import com.jessebrault.site.util.OpenGraph
>
<div class="article-container">
<article id="biography-article">
<%= renderBiography() %>
<Render item={biography} />
</article>
</div>
</StandardPage>

View File

@ -65,7 +65,7 @@ import com.jessebrault.site.util.OpenGraph
</WhenNotEmpty>
</section>
<section class="composition-text">
${composition.text.render()}
<Render item={composition.text} />
</section>
</article>
</div>

View File

@ -0,0 +1,21 @@
---
package com.jessebrault.site.developer
import com.jessebrault.site.StandardPage
import com.jessebrault.site.util.OpenGraph
---
<StandardPage
title={title}
description={description}
openGraph={<OpenGraph
title={pageTitle}
description={description}
path={selfPage.path}
/>}
>
<div class="article-container">
<article id="developer-article">
<Render item={developer} />
</article>
</div>
</StandardPage>

View File

@ -5,10 +5,10 @@ import com.jessebrault.site.StandardPage
import com.jessebrault.site.util.OpenGraph
---
<StandardPage
title={pageTitle}
title={title}
description={description}
openGraph={<OpenGraph
title={selfPage.name}
title={pageTitle}
description={description}
path={selfPage.path}
/>}
@ -16,7 +16,7 @@ import com.jessebrault.site.util.OpenGraph
>
<div class="article-container">
<article id="lessons-article">
<%= renderLessons() %>
<Render item={lessons} />
</article>
</div>
</StandardPage>

View File

@ -0,0 +1,21 @@
---
package com.jessebrault.site.resume
import com.jessebrault.site.StandardPage
import com.jessebrault.site.util.OpenGraph
---
<StandardPage
title={title}
description={description}
openGraph={<OpenGraph
title={pageTitle}
description={description}
path={selfPage.path}
/>}
>
<div class="article-container">
<article id="resume-article">
<Render item={resume} />
</article>
</div>
</StandardPage>

View File

@ -5,10 +5,10 @@ import com.jessebrault.site.StandardPage
import com.jessebrault.site.util.OpenGraph
---
<StandardPage
title={pageTitle}
title={title}
description={description}
openGraph={<OpenGraph
title={selfPage.name}
title={pageTitle}
description={description}
path={selfPage.path}
/>}
@ -16,7 +16,7 @@ import com.jessebrault.site.util.OpenGraph
>
<div class="article-container">
<article id="videos-article">
<%= renderVideos() %>
<Render item={videos} />
</article>
</div>
</StandardPage>

View File

@ -14,7 +14,7 @@ build {
basePackage 'com.jessebrault.site'
globals {
siteTagLine = 'Conductor, Composer, Developer'
menuItems = ['Biography', 'Compositions', 'Videos', 'Lessons', 'Contact']
menuItems = ['Biography', 'Compositions', 'Videos', 'Lessons', 'Résumé', 'Developer', 'Contact']
compositionCategories = ['Orchestra', 'Chamber', 'Solo', 'Wind Ensemble']
spotifyUrl = 'https://open.spotify.com/artist/4ea1gZnLlQTrXKIMsnlr45?si=DfR-KCDyTiycbjmYwu566w'
youtubeUrl = 'https://www.youtube.com/@JesseBrault0709'

View File

@ -50,10 +50,6 @@ header .titles h1 {
letter-spacing: 0.1em;
}
header .titles h2 {
display: none;
}
nav .bars-wrapper {
background: none;
padding: 0;
@ -130,6 +126,10 @@ article {
text-align: justify;
}
article :is(h1, h2, h3) {
text-align: left;
}
article h1 {
font-size: 1.5em;
}
@ -143,7 +143,8 @@ article h3 {
}
article ul {
padding-inline: 40px;
padding-left: 20px;
padding-right: 0;
text-align: left;
}
@ -201,7 +202,7 @@ a:hover {
align-items: center;
}
.headshot,
.portrait-photo,
.landscape-photo {
width: 100%;
}
@ -481,13 +482,6 @@ form.contact {
padding-inline: 30px;
}
header .titles h2 {
display: unset;
font-size: 2em;
font-size: 18px;
letter-spacing: 0.15em;
}
.banner,
.landscape-banner {
flex-direction: unset;
@ -512,6 +506,10 @@ form.contact {
font-size: 1.25em;
}
article ul {
padding-inline: 40px;
}
footer {
flex-direction: unset;
justify-content: space-between;
@ -569,22 +567,6 @@ form.contact {
}
}
@media screen and (max-width: 1023px) {
nav ul {
position: absolute;
left: 0;
z-index: 1;
width: 100%;
display: none;
flex-direction: column;
align-items: center;
row-gap: 10px;
padding: 20px;
background-color: var(--petrol);
border-bottom: 1px solid var(--charcoal);
}
}
@media screen and (min-width: 1024px) {
header {
align-items: center;
@ -596,18 +578,6 @@ form.contact {
letter-spacing: 0.175em;
}
header .titles h2 {
display: none;
}
nav .bars-wrapper {
display: none;
}
nav ul {
display: flex;
}
.banner,
.landscape-banner {
padding: 0 50px 50px;
@ -644,9 +614,29 @@ form.contact {
}
}
@media screen and (max-width: 1199px) {
nav ul {
position: absolute;
left: 0;
z-index: 1;
width: 100%;
display: none;
flex-direction: column;
align-items: center;
row-gap: 10px;
padding: 20px;
background-color: var(--petrol);
border-bottom: 1px solid var(--charcoal);
}
}
@media screen and (min-width: 1200px) {
header .titles h2 {
display: unset;
nav .bars-wrapper {
display: none;
}
nav ul {
display: flex;
}
.contact-banner svg {

View File

@ -1,9 +1,21 @@
const registerServiceWorker = async () => {
if ('serviceWorker' in navigator) {
try {
await navigator.serviceWorker.register('/serviceWorker.js', { scope: '/' })
} catch (error) {
console.error(`Service worker registration failed: ${error}`)
}
}
}
registerServiceWorker()
window.addEventListener('load', () => {
const navBars = document.getElementById('nav-bars')
const navItems = document.getElementById('nav-items')
const toggleMenu = () => {
if (window.matchMedia('screen and (max-width: 1023px)').matches) {
if (window.matchMedia('screen and (max-width: 1199px)').matches) {
if (navItems.style.display === 'flex') {
navItems.style.display = 'none'
} else {

18
static/serviceWorker.js Normal file
View File

@ -0,0 +1,18 @@
const addResourcesToCache = async () => {
const cache = await caches.open('v1')
cache.addAll(['/', '/compositions', '/videos', '/lessons', '/resume', '/developer', '/contact'])
;[
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait1.jpg',
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait2.jpg',
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/portrait4.jpg',
'https://jessebrault.nyc3.cdn.digitaloceanspaces.com/images/jesse-brault-bratislava.jpg'
].forEach(async url => {
const request = new Request(url, { mode: 'no-cors' })
const response = await fetch(request)
cache.put(request, response)
})
}
self.addEventListener('activate', event => {
event.waitUntil(addResourcesToCache())
})

File diff suppressed because it is too large Load Diff

57
texts/Developer.md Normal file
View File

@ -0,0 +1,57 @@
---
description: Information about Java, TypeScript, and web developer Jesse Brault.
---
# Jesse Brault: Software Developer
## Languages
- Java (5 years), Groovy (5 years), Kotlin (2 years)
- TypeScript (4 years), JavaScript (5 years), HTML, CSS/SCSS
- Lua, Ruby, Bash
## Frameworks, Libraries and Technologies
- Spring, Spring Boot, Gradle, JUnit, IntelliJ Idea
- NodeJS, React, Express, Prisma, GraphQL, VSCode
- MySQL, SQLite
## Programming Interests and Skills
- Web development
- Test-driven development
- Continuous integration and deployment
- REST protocol
- Dependency injection
- Programming languages
- Compilers
- Git, Github
- ORMs
- Open-source software
- Creating tools and plugins
## Personal Projects
All of my software projects are hosted publicly on GitHub. Please [contact](/contact) me if you have any inquiries related to them or my work in general.
### JVM
- [**groowt**, or **Groovy Web Tools**](https://github.com/JesseBrault0709/groowt): A set of libraries and CLI tools for facilitating web-development in Groovy, inspired by Ruby on Rails and Grails. Includes a custom templating language *Web View Components* inspired by ReactJS and Ruby's ERB templates, with a compiler developed with the ANTLR parser generator. 2024present. Java, Groovy, Kotlin.
- [**ssg**, or **Static Site Generator**](https://github.com/JesseBrault0709/ssg): An extensible CLI application for statically generating websites from text files and other input. I created this out of a need for a static site generator in which I could embed Groovy without all the hassle of a full-size server-oriented framework such as Grails. So far, I am using it for all my personal sites (including this very site), and in the past has been used elsewhere. 2023present. Groovy, Java.
- [**jbarchiva**](https://github.com/JesseBrault0709/jbarchiva): A Gradle plugin to facilitate easy access to my personal Maven repository at [archiva.jessebrault.com](https://archiva.jessebrault.com/). 2022-present.
- [**fsm**](https://github.com/JesseBrault0709/fsm): A set of easily configurable finite state machines with a simple domain-specific language, also featuring Groovy integration. Java, Groovy. 20222023.
### TypeScript
- [**jbci**, or **Jesse Brault Continuous Integration**](https://github.com/JesseBrault0709/jbci): A CI program written in TypeScript which listens to GitHub webhooks and then executes pre-configured shell scripts based on the webhook payload, mainly used to update my websites by simple pushes to my various GitHub repositories. 2022present.
- [**jb-api**](https://github.com/JesseBrault0709/jb-api): A backend for my personal websites which listens for JSON "contact" requests, validates them, and sends mail to personal e-mail addresses. 2022present.
- [**Meals Made Easy**](https://github.com/JesseBrault0709/MealsMadeEasy): A fully-functioning meal-planning web-app developed during my time in _Co.Lab | You Belong in Tech_. I worked closely with a product manager and designer to make the project come alive. It is built with TypeScript, React, Redux, and SCSS. 2021.
### Web Sites
- [Jesse Brault](https://github.com/JesseBrault0709/jb-ssg-site): Where you are right now. It is made using the above-described Groowt and Ssg libraries/utilities. 2024present.
- [Red Dog Ensemble](https://github.com/JesseBrault0709/reddog): A website for a contemporary classical music ensemble I co-founded in New York City, made using the above-mentioned Ssg. 2023. <a href="https://reddogensemble.com/" target="_blank">Visit site.</a>
## Soft Skills
- Cooperation
- Team player
- Leadership
- Agile development method
- Self-discipline
- Quick learning
- Proactive spirit
- Cross-functional teamwork
- Embracing of team diversity

79
texts/Resume.md Normal file
View File

@ -0,0 +1,79 @@
---
description: Online résumé of conductor and composer Jesse Brault.
---
# Résumé
For an official up-to-date résumé/CV, please [contact](/contact) me directly.
## Education
- **Master of Music**, Orchestral Conducting. The Juilliard School. 20152018.
- Private Teachers: Alan Gilbert, James Ross
- Guest teachers: Bernard Haitink, Gianandrea Noseda, David Zinman, Fabio Luisi, James Gaffigan.
- Relevant coursework: score reading, counterpoint, harmony, history.
- **Bachelor of Music**, Theory and Composition. St. Olaf College. *Summa cum laude*. 20092013.
- Private Teachers: Steven Amundson, Timothy Mahr, Justin Merritt.
- Relevant coursework: composition, theory, history, analysis.
## Conducting Experience
- **Private Chamber Orchestra Concert**, at the Juilliard School. December 2023.
- Conducted own work *Spirit Travels*
- **NDR Elbphilharmonie Orchester**: Assistant conductor and off-stage brass conductor for Alan Gilbert. June 2022.
- Strauss: *An Alpine Symphony*
- **Boston Symphony Orchestra**: Assistant conductor for Yuan Chang. January 2020.
- Chihchun Chi-sun Lee: *Formosan Triptych*
- Mozart: Piano concerto no. 25
- Tchaikovsky: Symphony no. 3
- **New Jersey Symphony Orchestra**: Assistant conductor for Xian Zhang. December 2019.
- Anna Clyne: *Within Her Arms*
- Rachmaninoff: Piano concerto no. 3
- Dvořák: Symphony no. 8
- **St. Louis Symphony Orchestra**: Assistant conductor for Andrew Grams. November 2019.
- Tchaikovsky: *The Nutcracker*
- **Oedipus in the District**: Music director for a contemporary one-act opera performed twice at The Tank, New York, NY. October 2019.
- Josh Getman: *Oedipus in the District*
- **Red Dog Ensemble**: Conductor and co-founder. Inaugural recording of album *Neon and Oak* at Avaloch Farm Institute, Boscawen, New Hampshire. October 2019.
- Jon Cziner: *flowers of fire*
- Tom Morrison: *Neon and Oak*
- Sato Matsui: *The Imperfect Storm*
- Will Stackpole: *L'abîme*
- **Nord Anglia Performing Arts Festival**: Bratislava, Slovakia. Guest conductor. March 2019.
- Programmed music for two youth orchestras (ages 1018) and a combined orchestra/chorus work
- Prepared parts for distribution to students in advance of festival
- Worked closely with festival staff during planning and execution stages
- Led and conducted all rehearsals and final concert
- **Ensemble Connect**: Guest conductor. March 2019.
- John Adams: *Chamber Symphony*
- **National Youth Symphony**: Prep-conductor for side-by-side with Daniel Harding and Royal Concertgebouw Orchestra.
February 2019.
- Schumann: *Manfred Overture*
- **Graduation Recital**, The Juilliard School. May 2018.
- Sibelius: *Valse triste*
- Mahler: Symphony no. 4 (arr. Stein)
- **Orlando Philharmonic**: Assistant conductor for Eric Jacobson. October 2017.
## Composition Performances
- *Spirit Travels*, at The Juilliard School. December 2023.
- *Capriccioso*, played on-air by NPO Klassiek (Netherlands). [Listen here at 24:20](https://www.npoklassiek.nl/uitzendingen/podium/cb3c4d56-6c59-4100-8a92-b6081daf9719/2022-03-19-podium)
- *Capriccioso*, at the Mauritshuis, The Hague, Netherlands. Recorded February 2022 and released live in March 2022.
- *Near, Under, Far*, online performance by cellist Gabriel Cabezas from New York, New York. October 2021.
- *Arcadia*, online performance by Julia Hamos and Stephen Waarts from Berlin, Germany. November 2020.
- *Conversations*, performed by Jocelyn Zhu and Mariella Haubs on WQXR, New York, New York. April 2019.
- *Conversations*, world premiere in New York, New York by Jocelyn Zhu and Mariella Haubs. April 2019.
- *Sonata Shambhala*, premieres at Marlboro College, Vermont; Seattle, Washington; and Camphill Ghent, New York;
by Benjamin Hochman. November 2018.
## Album
- **Neon and Oak**: recorded with [Red Dog Ensemble](https://reddogensemble.com/) and released in March 2023. Co-edited with Will Stackpole. Features four new works by emerging composers. See [here](https://reddogensemble.com/album.html) for more information.
## Teaching Experience
- **Juilliard Pre-College**: Substitute teacher for music theory, counterpoint, and ear training. 20182020.
- **Fordham University**: Adjunct professor and Fordham Lincoln Center Chamber Orchestra conductor. 20182019.
- **Juilliard Music Advancement Program**: Substitute teacher for music theory, ear training, and history. 20182019.
- **New York Youth Symphony**: Substitute teacher for private composition lessons. 2019.
- **St. Olaf College**: Teaching assistant for Steven Amundson, music theory. 20102013.
- **Private Tutor**, St. Olaf College: for ear training. 20112013.
## Instruments
- Trombone
- Piano
- Organ