Hayden Goldsworthy

Workshops

Small groups, out shooting. I teach camera movement, night skies and getting more from Fujifilm cameras, on my own and through FUJIFILM events.

Group workshops through FUJIFILMMy group workshops run as FUJIFILM events. Dates, cities and sign-ups are all on FUJIFILM's events page.See FUJIFILM events ↗
DateWhereWorkshopFormatStatus
Nov 14 2026Indianapolis, INMoving the cameraWalk · 3 h3 seats leftReserve
Dec 05 2026Chicago, ILFilm simulations in practiceFUJIFILM eventOpenRegister ↗
Jan 23 2027Southern IndianaNight sky, start to finishField · 6 hOpenReserve
Jun 10 2026Theme parkRides at an eighth of a secondField · 4 hPast

Moving the camera

Three hours on foot, from golden hour to dark. We slow the shutter to an eighth of a second and use the camera's motion as the brush: pans, vertical drags and zoom bursts. Bring any camera with manual shutter control. Fujifilm loaners available.

  • Shutter speeds from 1/30 to 1 second, and when each one works
  • Panning a walker so the face stays sharp
  • Building a sequence instead of a single frame
  • Exporting to the 3:4 sheet for print and Instagram

One-on-one sessions

Just you and me, out shooting. Tell me when you're free, what camera you have and what you want to get better at.

Email me to book a session→goldsworthymedia@gmail.com
© 2026 Hayden Goldsworthy · Indianagoldsworthymedia@gmail.comInstagram ↗

import * as React from "react"

import { addPropertyControls, ControlType } from "framer"


// Hayden Goldsworthy site header: menu button, tracked wordmark, page links,

// and a drawer that holds navigation plus the About section.


const T = {

ink: "#0F0E0C",

carbon: "#171513",

graphite: "#2B2824",

ash: "#8D877D",

bone: "#E9E4DA",

paper: "#FFFFFF",

}

const SERIF = '"Instrument Serif", "Iowan Old Style", Georgia, serif'

const SANS = '"Geist", ui-sans-serif, system-ui, -apple-system, sans-serif'

const MONO = '"Geist Mono", ui-monospace, Menlo, monospace'


function useFonts() {

React.useEffect(() => {

if (typeof document === "undefined" || document.getElementById("hg-fonts")) return

const l = document.createElement("link")

l.id = "hg-fonts"

l.rel = "stylesheet"

l.href =

"https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500&family=Geist+Mono:wght@400;500&family=Doto:wght@800&display=swap"

document.head.appendChild(l)

}, [])

}


function useWidth(ref: React.RefObject<HTMLElement>) {

const [w, setW] = React.useState(1200)

React.useLayoutEffect(() => {

if (!ref.current) return

const ro = new ResizeObserver((e) => setW(e[0].contentRect.width))

ro.observe(ref.current)

return () => ro.disconnect()

}, [])

return w

}


function isCurrent(link?: string) {

if (!link || typeof window === "undefined") return false

try {

const p = new URL(link, window.location.href).pathname.replace(/\/$/, "")

const here = window.location.pathname.replace(/\/$/, "")

return p === here && p !== ""

} catch {

return false

}

}


type NavLink = { label: string; link?: string }


/**

* @framerSupportedLayoutWidth any-prefer-fixed

* @framerSupportedLayoutHeight auto

*/

export default function SiteHeader(props) {

useFonts()

const {

name,

homeLink,

links = [],

aboutTitle,

aboutText,

portrait,

cameras,

lenses,

socialLabel,

socialLink,

contactNote,

email,

} = props

const ref = React.useRef<HTMLDivElement>(null)

const width = useWidth(ref)

const phone = width < 700

const [open, setOpen] = React.useState(false)


React.useEffect(() => {

if (!open) return

const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false)

window.addEventListener("keydown", onKey)

return () => window.removeEventListener("keydown", onKey)

}, [open])


const list = (s?: string) =>

(s || "")

.split("\n")

.map((x) => x.trim())

.filter(Boolean)


return (

<div ref={ref} style={{ ...props.style, width: "100%", background: T.ink, color: T.bone }}>

<div

style={{

display: "grid",

gridTemplateColumns: "1fr auto 1fr",

alignItems: "center",

height: 48,

padding: "0 14px",

}}

>

<button

aria-label="Open menu"

onClick={() => setOpen(true)}

style={{

justifySelf: "start",

width: 36,

height: 36,

background: "none",

border: 0,

cursor: "pointer",

display: "grid",

alignContent: "center",

gap: 5,

padding: 0,

}}

>

<i style={{ display: "block", width: 18, height: 1, background: T.bone }} />

<i style={{ display: "block", width: 18, height: 1, background: T.bone }} />

</button>

<a

href={homeLink || "/"}

style={{

fontFamily: SERIF,

fontSize: phone ? 13 : 15,

letterSpacing: phone ? "0.32em" : "0.42em",

paddingLeft: phone ? "0.32em" : "0.42em",

textTransform: "uppercase",

color: T.bone,

textDecoration: "none",

whiteSpace: "nowrap",

}}

>

{name}

</a>

<nav

aria-label="Main"

style={{

justifySelf: "end",

display: phone ? "none" : "flex",

gap: 20,

fontFamily: SANS,

fontSize: 13,

}}

>

{links.map((l: NavLink, i: number) => (

<a

key={i}

href={l.link}

style={{ color: isCurrent(l.link) ? T.bone : T.ash, textDecoration: "none" }}

>

{l.label}

</a>

))}

</nav>

</div>


{/* drawer */}

<div

onClick={() => setOpen(false)}

style={{

position: "fixed",

inset: 0,

zIndex: 900,

background: "rgba(15,14,12,.6)",

opacity: open ? 1 : 0,

pointerEvents: open ? "auto" : "none",

transition: "opacity .3s",

}}

/>

<aside

aria-hidden={!open}

style={{

position: "fixed",

top: 0,

bottom: 0,

left: 0,

zIndex: 901,

width: "min(100%, 560px)",

background: T.ink,

borderRight: `1px solid ${T.graphite}`,

transform: open ? "none" : "translateX(-102%)",

// hidden when closed so it never shows beside the page on the canvas

visibility: open ? "visible" : "hidden",

transition: `transform .35s cubic-bezier(.2,.7,.2,1), visibility 0s linear ${open ? "0s" : ".35s"}`,

overflowY: "auto",

padding: "14px 28px 28px",

display: "flex",

flexDirection: "column",

gap: 36,

color: T.bone,

fontFamily: SANS,

}}

>

<button

aria-label="Close menu"

onClick={() => setOpen(false)}

style={{ alignSelf: "flex-start", background: "none", border: 0, color: T.bone, fontSize: 22, cursor: "pointer", padding: "6px 0" }}

>

×

</button>

<nav style={{ display: "flex", flexDirection: "column", gap: 2 }}>

{[{ label: "Home", link: homeLink || "/" }, ...links].map((l: NavLink, i: number) => (

<a

key={i}

href={l.link}

onClick={() => setOpen(false)}

style={{

fontFamily: SERIF,

fontSize: 40,

lineHeight: 1.1,

color: isCurrent(l.link) ? T.bone : T.ash,

textDecoration: "none",

}}

>

{l.label}

</a>

))}

</nav>

<div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 20, alignItems: "start" }}>

<div style={{ background: T.paper, padding: 2, borderRadius: 12, aspectRatio: "3 / 4" }}>

{portrait?.src ? (

<img

src={portrait.src}

srcSet={portrait.srcSet}

alt={portrait.alt || "Portrait"}

style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: 10, display: "block" }}

/>

) : (

<div

style={{

width: "100%",

height: "100%",

borderRadius: 10,

background: T.carbon,

display: "grid",

placeItems: "center",

fontFamily: MONO,

fontSize: 10,

color: T.ash,

}}

>

Portrait

</div>

)}

</div>

<div>

<div style={{ fontFamily: SERIF, fontSize: 40, lineHeight: 1 }}>{aboutTitle}</div>

<p style={{ fontSize: 13.5, color: T.ash, marginTop: 10, lineHeight: 1.55 }}>{aboutText}</p>

</div>

</div>

<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 24 }}>

{[

["Cameras", list(cameras)],

["Lenses", list(lenses)],

].map(([label, items]: any) => (

<div key={label}>

<span style={{ fontFamily: MONO, fontSize: 10.5, letterSpacing: ".12em", textTransform: "uppercase", color: T.ash }}>

{label}

</span>

<ul style={{ listStyle: "none", margin: "8px 0 0", padding: 0, fontFamily: SERIF, fontSize: 19, lineHeight: 1.45 }}>

{items.map((x: string) => (

<li key={x}>{x}</li>

))}

</ul>

</div>

))}

</div>

<div style={{ marginTop: "auto", display: "flex", flexDirection: "column", gap: 6, fontSize: 13, color: T.ash }}>

{contactNote && <span>{contactNote}</span>}

{email && (

<a href={`mailto:${email}`} style={{ color: T.bone }}>

{email}

</a>

)}

{socialLink && (

<a href={socialLink} target="_blank" rel="noopener" style={{ color: T.bone }}>

{socialLabel} ↗

</a>

)}

</div>

</aside>

</div>

)

}


SiteHeader.defaultProps = {

name: "Hayden Goldsworthy",

homeLink: "/",

links: [

{ label: "Portfolio", link: "/portfolio" },

{ label: "Prints", link: "/prints" },

{ label: "Workshops", link: "/workshops" },

{ label: "Film Sims", link: "/film-sims" },

],

aboutTitle: "Hi, I'm Hayden.",

aboutText:

"I'm a photographer and filmmaker in Indiana, and a FUJIFILM Product Technical Expert. I've spent more than a decade telling stories with cameras, from documentaries and commercial films to teaching. Most of my work slows the shutter down: moving the camera, stacking exposures, waiting for the sky.",

cameras: "X-E5\nX-T5\nGFX100RF\nGFX100 II",

lenses: "XF35mm F1.4\nXF90mm F2\nXF70-300mm\nXF500mm F5.6\nXF8mm F3.5",

socialLabel: "@fujiworthy",

socialLink: "https://www.instagram.com/fujiworthy/",

contactNote: "For prints, workshops and commissions, email me.",

email: "goldsworthymedia@gmail.com",

}


addPropertyControls(SiteHeader, {

name: { type: ControlType.String, title: "Name" },

homeLink: { type: ControlType.Link, title: "Home link" },

links: {

type: ControlType.Array,

title: "Links",

control: {

type: ControlType.Object,

controls: {

label: { type: ControlType.String, title: "Label" },

link: { type: ControlType.Link, title: "Link" },

},

},

},

aboutTitle: { type: ControlType.String, title: "About title" },

aboutText: { type: ControlType.String, title: "About text", displayTextArea: true },

portrait: { type: ControlType.ResponsiveImage, title: "Portrait" },

cameras: { type: ControlType.String, title: "Cameras", displayTextArea: true },

lenses: { type: ControlType.String, title: "Lenses", displayTextArea: true },

contactNote: { type: ControlType.String, title: "Footnote", displayTextArea: true },

email: { type: ControlType.String, title: "Email" },

socialLabel: { type: ControlType.String, title: "Social label" },

socialLink: { type: ControlType.Link, title: "Social link" },

})