You're pretty close.
First, I'd encourage you to use more descriptive variable names rather than generic ones like $table_name and $table_name2. It's easy to get confused with non-descriptive names. Imagine what would happen if you had five or six tables to join.
I believe c_cav and I were both referring to the "person table" as "$table_name" and the "marriage table" as "table_name2".
With c_cav's SQL, the substitution should be as simple as:
SELECT DISTINCT
a.first_name AS spouse1_first_name,
a.family_name AS spouse1_family_name,
j.date_of_marriage,
j.marriage_id,
b.first_name AS spouse2_first_name,
b.family_name AS spouse2_family_name
FROM $table_name2 j
INNER JOIN $table_name a
ON a.id = j.person_id OR a.id = j.spouse_id
INNER JOIN $table_name2 b
ON b.id = j.person_id OR b.id = j.spouse_id
Notice how only three substitutions for $table_name and $table_name2 were required: one in the FROM clause and two in the JOIN clauses. This is possible because we're using the table aliases 'a', 'b', and 'j', so you only need to define the alias once, then you can use the aliases everywhere else in the SQL. You can use aliases on tables or columns. The "AS" keyword is optional, but I prefer it for readability.
On that note, I think c_cav arbitrarily chose 'a', 'b', and 'j' for example purposes. It works, but again, the letters 'a', 'b', and 'j' are no more descriptive than "table_name" and "table_name2". In your actual code, you'd probably want to use something more descriptive like 'p' for person and 'm' for marriage... or just 'person' and 'marriage'. Whatever is best for readability.
When you want to get the value of spouse 1's first name, you'll use the column name "spouse1_first_name", not "first_name" or "a.first_name".
Hope that helps.