Order now! Download, DVD or USB. Upgrades

Discover Your Family Story With Family Tree Maker!

FTM 2024 for Mac and Windows

For 35 years Family Tree Maker has been the world's favorite genealogy software making it easier than ever to discover your family story, preserve your legacy and share your unique heritage. If you're new to family history, you'll appreciate how this intuitive program lets you easily grow your family tree with simple navigation, tree-building tools, and integrated Web searching. If you're already an expert, you can dive into the more advanced features, options for managing data, and a wide variety of charts and reports. The end result is a family history that you and your family will treasure for years to come!

buy now

Have your relatives fact-check your tree with the free Connect mobile app.

Key Product Features

  • Easy tree building
  • Single click synchronization with Ancestry.com®
  • Hints from Ancestry and FamilySearch
  • Tree fact-checking by relatives in real-time

-- -------------------------------------------- -- 2. TABLE UTILITIES -- -------------------------------------------- local table_utils = {}

-- -------------------------------------------- -- 1. STRING UTILITIES -- -------------------------------------------- local string_utils = {}

-- Trim whitespace from both ends function string_utils.trim(str) return str:match("^%s*(.-)%s*$") end

-- Check if value is a number within range function validate.is_in_range(val, min, max) return type(val) == "number" and val >= min and val <= max end

-- Deep copy a table (handles nested tables) function table_utils.deep_copy(orig) local copy if type(orig) == "table" then copy = {} for k, v in pairs(orig) do copy[table_utils.deep_copy(k)] = table_utils.deep_copy(v) end setmetatable(copy, table_utils.deep_copy(getmetatable(orig))) else copy = orig end return copy end

-- Log with timestamp function debug_utils.log(message, level) level = level or "INFO" local timestamp = os.date("%Y-%m-%d %H:%M:%S") print(string.format("[%s] [%s] %s", timestamp, level, message)) end

file_utils.write_file("test.txt", "Lua rocks!") print(file_utils.read_file("test.txt"))

-- Check if a value exists in a table (linear search) function table_utils.contains(tbl, value) for _, v in pairs(tbl) do if v == value then return true end end return false end

-- Split string by delimiter (returns table) function string_utils.split(str, delimiter) local result = {} local pattern = string.format("([^%s]+)", delimiter) for match in str:gmatch(pattern) do table.insert(result, match) end return result end

-- Check if string ends with a suffix function string_utils.ends_with(str, suffix) return #suffix == 0 or str:sub(-#suffix) == suffix end

Luar | Script

-- -------------------------------------------- -- 2. TABLE UTILITIES -- -------------------------------------------- local table_utils = {}

-- -------------------------------------------- -- 1. STRING UTILITIES -- -------------------------------------------- local string_utils = {}

-- Trim whitespace from both ends function string_utils.trim(str) return str:match("^%s*(.-)%s*$") end script luar

-- Check if value is a number within range function validate.is_in_range(val, min, max) return type(val) == "number" and val >= min and val <= max end

-- Deep copy a table (handles nested tables) function table_utils.deep_copy(orig) local copy if type(orig) == "table" then copy = {} for k, v in pairs(orig) do copy[table_utils.deep_copy(k)] = table_utils.deep_copy(v) end setmetatable(copy, table_utils.deep_copy(getmetatable(orig))) else copy = orig end return copy end -- -------------------------------------------- -- 2

-- Log with timestamp function debug_utils.log(message, level) level = level or "INFO" local timestamp = os.date("%Y-%m-%d %H:%M:%S") print(string.format("[%s] [%s] %s", timestamp, level, message)) end

file_utils.write_file("test.txt", "Lua rocks!") print(file_utils.read_file("test.txt")) = min and val &lt

-- Check if a value exists in a table (linear search) function table_utils.contains(tbl, value) for _, v in pairs(tbl) do if v == value then return true end end return false end

-- Split string by delimiter (returns table) function string_utils.split(str, delimiter) local result = {} local pattern = string.format("([^%s]+)", delimiter) for match in str:gmatch(pattern) do table.insert(result, match) end return result end

-- Check if string ends with a suffix function string_utils.ends_with(str, suffix) return #suffix == 0 or str:sub(-#suffix) == suffix end

Family Tree Maker includes:

  • Everything you need to begin your journey through your family's history
  • A variety of charts and dozens of reports
  • Themed backgrounds, borders, and embellishments collection for printing
  • Locations database with more than 3 million place names for consistent data entry
  • Access to online street and satellite maps
  • Digital version of the Companion Guide
  • Convenient onscreen Help system
trees

Family Tree Maker Community

The Family Tree Maker Community is a collection of helpful people and resources including:
Click here to learn more...
FTM Community

Minimum System Requirements

Mac

macOS Big Sur 11 and later, including macOS Tahoe 26, 900 MB hard disk space, 4 GB of RAM (8 GB recommended), 1280 x 800 screen resolution.

Windows

Windows 10 (64-bit) or later, including Windows 11, 800 MB hard disk space, 2 GB of RAM (4 GB recommended), 1024 x 768 screen resolution.

Ribbon
Gift Collection
Family Tree Maker logo

GIFT COLLECTION

Gift Collection
FAQ

This FAQ provides answers to common questions about Family Tree Maker.